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/bin/gnng.py ADDED
@@ -0,0 +1,434 @@
1
+ #!/usr/bin/env python
2
+
3
+ """
4
+ gnng - Fetches network devices interfaces and displays them in a table view.
5
+
6
+ Fetches interface information from routing and firewall devices. This includes
7
+ network and IP information along with the inbound and outbound filters that
8
+ may be applied to the interface. Works on Juniper, Netscreen, Foundry, and Cisco
9
+ devices.
10
+ """
11
+
12
+ __version__ = "1.3.2"
13
+
14
+ import csv
15
+ import os
16
+ import sys
17
+ from collections import namedtuple
18
+ from optparse import OptionParser
19
+ from sqlite3 import dbapi2 as sqlite
20
+
21
+ import prettytable
22
+
23
+ from trigger.cmds import NetACLInfo
24
+
25
+ # Put this here until the default changes to not load ACLs from redis.
26
+ from trigger.conf import settings
27
+ from trigger.netdevices import NetDevices, device_match
28
+
29
+ settings.WITH_ACLS = False
30
+
31
+ # log.startLogging(sys.stdout, setStdout=False)
32
+
33
+ # Constants
34
+ DEBUG = os.getenv("DEBUG")
35
+ MAX_CONNS = 10
36
+ ROW_LABELS = ["Interface", "Addresses", "Subnets", "ACLs IN", "ACLs OUT", "Description"]
37
+
38
+ # Namedtuples
39
+ RowData = namedtuple("RowData", "all_rows subnet_table")
40
+ DottyData = namedtuple("DottyData", "graph links")
41
+
42
+
43
+ def parse_args(argv):
44
+ parser = OptionParser(
45
+ usage="%prog [options] [routers]",
46
+ description="""GetNets-NG
47
+
48
+ Fetches interface information from routing and firewall devices. This includes
49
+ network and IP information along with the inbound and outbound filters that
50
+ may be applied to the interface. Skips un-numbered and disabled interfaces by
51
+ default. Works on Cisco, Foundry, Juniper, and NetScreen devices.""",
52
+ )
53
+ parser.add_option("-a", "--all", action="store_true", help="run on all devices")
54
+ parser.add_option(
55
+ "-c",
56
+ "--csv",
57
+ action="store_true",
58
+ help="output the data in CSV format instead.",
59
+ )
60
+ parser.add_option(
61
+ "-d",
62
+ "--include-disabled",
63
+ action="store_true",
64
+ help="include disabled interfaces.",
65
+ )
66
+ parser.add_option(
67
+ "-u",
68
+ "--include-unnumbered",
69
+ action="store_true",
70
+ help="include un-numbered interfaces.",
71
+ )
72
+ parser.add_option(
73
+ "-j",
74
+ "--jobs",
75
+ type="int",
76
+ default=MAX_CONNS,
77
+ help="maximum simultaneous connections to maintain.",
78
+ )
79
+ parser.add_option(
80
+ "-N",
81
+ "--nonprod",
82
+ action="store_false",
83
+ default=True,
84
+ help="Include non-production devices from the query or "
85
+ "[routers]. Requires a legitimate query.",
86
+ )
87
+ parser.add_option("-s", "--sqldb", type="str", help="output to SQLite DB")
88
+ parser.add_option(
89
+ "",
90
+ "--dotty",
91
+ action="store_true",
92
+ help="output connect-to information in dotty format.",
93
+ )
94
+ parser.add_option(
95
+ "",
96
+ "--filter-on-group",
97
+ action="append",
98
+ help="Run on all devices owned by this group",
99
+ )
100
+ parser.add_option(
101
+ "",
102
+ "--filter-on-type",
103
+ action="append",
104
+ help="Run on all devices with this device type",
105
+ )
106
+
107
+ opts, args = parser.parse_args(argv)
108
+
109
+ if len(args) == 1 and not opts.all and not opts.filter_on_type:
110
+ parser.print_help()
111
+ sys.exit(1)
112
+
113
+ return opts, args
114
+
115
+
116
+ def fetch_router_list(args, opts):
117
+ """Turns a list of device names into device objects, skipping unsupported,
118
+ invalid, or filtered devices."""
119
+ nd = NetDevices(production_only=opts.nonprod)
120
+ ret = []
121
+ blocked_groups = []
122
+ if args:
123
+ for arg in args:
124
+ # Try to find the device, but fail gracefully if it can't be found
125
+ device = device_match(arg)
126
+ if not pass_filters(device, opts) or device is None:
127
+ continue
128
+ ret.append(device)
129
+
130
+ else:
131
+ for entry in nd.values():
132
+ if entry.owningTeam in blocked_groups:
133
+ continue
134
+ if not pass_filters(entry, opts):
135
+ continue
136
+ ret.append(entry)
137
+
138
+ return sorted(ret, reverse=True)
139
+
140
+
141
+ def pass_filters(device, opts):
142
+ """Used by fetch_router_list() to filter a device based on command-line arguments."""
143
+ if opts.filter_on_group:
144
+ if device.owningTeam not in opts.filter_on_group:
145
+ return False
146
+ if opts.filter_on_type:
147
+ if device.deviceType not in opts.filter_on_type:
148
+ return False
149
+
150
+ return True
151
+
152
+
153
+ def write_sqldb(sqlfile, dev, rows):
154
+ """Write device fields to sqlite db"""
155
+ create_table = False
156
+
157
+ if not os.path.isfile(sqlfile):
158
+ create_table = True
159
+
160
+ connection = sqlite.connect(sqlfile)
161
+ cursor = connection.cursor()
162
+
163
+ if create_table:
164
+ # if the db doesn't exist we want to create the table.
165
+ cursor.execute("""
166
+ CREATE TABLE dev_nets (
167
+ id INTEGER PRIMARY KEY,
168
+ insert_date DATE,
169
+ device_name VARCHAR(128),
170
+ iface_name VARCHAR(32),
171
+ iface_addrs VARCHAR(1024),
172
+ iface_subnets VARCHAR(1024),
173
+ iface_inacl VARCHAR(32),
174
+ iface_outacl VARCHAR(32),
175
+ iface_descr VARCHAR(1024)
176
+ );
177
+ """)
178
+ cursor.execute("""
179
+ CREATE TRIGGER auto_date AFTER INSERT ON dev_nets
180
+ BEGIN
181
+ UPDATE dev_nets SET insert_date = DATETIME('NOW')
182
+ WHERE rowid = new.rowid;
183
+ END;
184
+ """)
185
+
186
+ for row in rows:
187
+ iface, addrs, snets, inacl, outacl, desc = row
188
+ cursor.execute(
189
+ f"""
190
+ INSERT INTO dev_nets (
191
+ device_name,
192
+ iface_name,
193
+ iface_addrs,
194
+ iface_subnets,
195
+ iface_inacl,
196
+ iface_outacl,
197
+ iface_descr )
198
+ VALUES (
199
+ '{dev}', '{iface}', '{addrs}',
200
+ '{snets}', '{inacl}', '{outacl}', '{desc}'
201
+ );"""
202
+ )
203
+
204
+ connection.commit()
205
+ cursor.close()
206
+ connection.close()
207
+
208
+
209
+ def get_interface_data(devices, production_only=True, max_conns=MAX_CONNS, opts=None):
210
+ """
211
+ Fetch interface information from ``devices`` and return it as a dict.
212
+
213
+ :param devices:
214
+ List of device hostnames
215
+
216
+ :param production_only:
217
+ Whether to include only devices marked as "PRODUCTION"
218
+
219
+ :param max_conns:
220
+ Max number of simultaneous connections
221
+ """
222
+ skip_disabled = not opts.include_disabled # Inverse of include is skip :D
223
+ ninfo = NetACLInfo(
224
+ devices=devices,
225
+ production_only=production_only,
226
+ max_conns=max_conns,
227
+ skip_disabled=skip_disabled,
228
+ )
229
+ ninfo.run()
230
+ if DEBUG:
231
+ print("NetACLInfo done!")
232
+
233
+ return ninfo.config
234
+
235
+
236
+ def build_output(main_data, opts, labels=None):
237
+ """
238
+ Iterate the interface data, then build and return row data.
239
+
240
+ :param main_data:
241
+ Dictionary of interface data
242
+
243
+ :param opts:
244
+ OptionParser object
245
+
246
+ :param labels:
247
+ Row labels for table output
248
+ """
249
+ if labels is None:
250
+ labels = ROW_LABELS
251
+
252
+ subnet_table = {}
253
+ all_rows = {}
254
+
255
+ for dev, data in main_data.items():
256
+ rows = []
257
+ interfaces = sorted(data)
258
+ for interface in interfaces:
259
+ iface = data[interface]
260
+
261
+ # Maybe skip down interfaces
262
+ if "addr" not in iface and not opts.include_disabled:
263
+ continue
264
+
265
+ if DEBUG:
266
+ print(">>> ", interface)
267
+
268
+ addrs = iface["addr"]
269
+ subns = iface["subnets"]
270
+ acls_in = iface["acl_in"]
271
+ acls_out = iface["acl_out"]
272
+ desctext = " ".join(iface.get("description")).replace(" : ", ":")
273
+
274
+ # Maybe skip un-numbered interfaces
275
+ if not addrs and not opts.include_unnumbered:
276
+ continue
277
+
278
+ # Trim the description
279
+ if not opts.csv:
280
+ desctext = desctext[0:50]
281
+
282
+ addresses = []
283
+ subnets = []
284
+
285
+ for a in addrs:
286
+ addresses.append(a.strNormal())
287
+
288
+ for s in subns:
289
+ subnets.append(s.strNormal())
290
+
291
+ if s in subnet_table:
292
+ subnet_table[s].append((dev, interface, addrs))
293
+ else:
294
+ subnet_table[s] = [(dev, interface, addrs)]
295
+
296
+ if DEBUG:
297
+ print("\t in:", acls_in)
298
+ print("\t ou:", acls_out)
299
+ rows.append(
300
+ [
301
+ interface,
302
+ " ".join(addresses),
303
+ " ".join(subnets),
304
+ "\n".join(acls_in),
305
+ "\n".join(acls_out),
306
+ desctext,
307
+ ]
308
+ )
309
+
310
+ all_rows[dev.nodeName] = rows
311
+
312
+ return RowData(all_rows, subnet_table)
313
+
314
+
315
+ def handle_output(all_rows, opts):
316
+ """
317
+ Do stuff with the output data.
318
+
319
+ :param all_rows:
320
+ A list of lists of row data
321
+
322
+ :param opts:
323
+ OptionParser object
324
+ """
325
+ for dev, rows in all_rows.items():
326
+ if opts.csv:
327
+ writer = csv.writer(sys.stdout)
328
+ for row in rows:
329
+ writer.writerow([dev] + row)
330
+ elif opts.dotty:
331
+ continue
332
+ elif opts.sqldb:
333
+ write_sqldb(opts.sqldb, dev, rows)
334
+ else:
335
+ print(f"DEVICE: {dev}")
336
+ print_table(rows)
337
+
338
+
339
+ def print_table(rows, labels=None):
340
+ """
341
+ Print the interface table for a device
342
+ """
343
+ if labels is None:
344
+ labels = ROW_LABELS
345
+
346
+ output_table = prettytable.PrettyTable()
347
+ output_table.field_names = labels
348
+ output_table.align = "l"
349
+ output_table.vrules = prettytable.prettytable.ALL
350
+ output_table.hrules = prettytable.prettytable.HEADER
351
+
352
+ for row in rows:
353
+ row = [x.strip() for x in row]
354
+ output_table.add_row(row)
355
+
356
+ print(output_table)
357
+ print("")
358
+
359
+
360
+ def output_dotty(subnet_table, display=True):
361
+ """
362
+ Output and return dotty config for a ``subnet_table``
363
+
364
+ :param subnet_table:
365
+ Dict mapping subnets to devices and interfaces
366
+ """
367
+ links = {}
368
+
369
+ for ip, devs in subnet_table.items():
370
+ if len(devs) > 1:
371
+ router1 = devs[0][0]
372
+ router2 = devs[1][0]
373
+
374
+ kf1 = router1 in links
375
+ kf2 = router2 in links
376
+
377
+ if kf1:
378
+ if router2 not in links[router1]:
379
+ links[router1].append(router2)
380
+
381
+ elif kf2:
382
+ if router1 not in links[router2]:
383
+ links[router2].append(router1)
384
+
385
+ else:
386
+ links[router1] = [router2]
387
+
388
+ if not links:
389
+ print("No valid links for dotty generation.")
390
+ return None
391
+
392
+ NetDevices() # This uses the pre-existing NetDevices singleton
393
+
394
+ graph = """graph network {
395
+ overlap=scale; center=true; orientation=land;
396
+ resolution=0.10; rankdir=LR; ratio=fill;
397
+ node [fontname=Courier, fontsize=10]"""
398
+
399
+ for leaf, subleaves in links.items():
400
+ for subleaf in subleaves:
401
+ graph += f'"{leaf.shortName}"--"{subleaf.shortName}"\n'
402
+ # print >>sys.stderr, leaf,"connects to: ",','.join(subleaves)
403
+ graph += "\n}"
404
+
405
+ if display:
406
+ print(graph)
407
+
408
+ return DottyData(graph, links)
409
+
410
+
411
+ def main():
412
+ """Main entry point for the CLI tool."""
413
+ opts, args = parse_args(sys.argv)
414
+
415
+ if opts.all or opts.filter_on_type:
416
+ routers = fetch_router_list(None, opts)
417
+ else:
418
+ routers = fetch_router_list(args[1:], opts)
419
+
420
+ if not routers:
421
+ sys.exit(1)
422
+
423
+ main_data = get_interface_data(
424
+ devices=routers, production_only=opts.nonprod, opts=opts
425
+ )
426
+ all_rows, subnet_table = build_output(main_data, opts)
427
+ handle_output(all_rows, opts)
428
+
429
+ if opts.dotty:
430
+ output_dotty(subnet_table)
431
+
432
+
433
+ if __name__ == "__main__":
434
+ main()
trigger/bin/gong.py ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ gong (go ng) - Command-line client to log in to network devices using TACACS credentials.
5
+
6
+ An optional .gorc file may be used to specify user preferences.
7
+
8
+ Partially adapted from conch. See twisted.conch.scripts.conch and
9
+ http://twistedmatrix.com/projects/conch/documentation/howto/conch_client.html
10
+ """
11
+
12
+ __version__ = "2.0"
13
+
14
+ import os
15
+ import sys
16
+ from optparse import OptionParser
17
+
18
+ from twisted.python import log
19
+
20
+ # Put this here until the default changes to not load ACLs from redis.
21
+ from trigger.conf import settings
22
+ from trigger.netdevices import device_match
23
+
24
+ settings.WITH_ACLS = False
25
+
26
+
27
+ def parse_args(argv):
28
+ parser = OptionParser(
29
+ usage="%prog [options] [device]",
30
+ description="""\
31
+ Automatically log into network devices using cached TACACS credentials.
32
+ """,
33
+ )
34
+
35
+ parser.add_option(
36
+ "-o", "--oob", action="store_true", help="Connect to device out of band first."
37
+ )
38
+
39
+ opts, args = parser.parse_args(argv)
40
+
41
+ if len(args) != 2:
42
+ parser.print_help()
43
+ sys.exit(2)
44
+
45
+ return opts, args
46
+
47
+
48
+ def connect_to_oob(dev):
49
+ """Lookup out-of-band info and try to connect to the console using telnet."""
50
+ tn = f"telnet {dev.OOBTerminalServerFQDN} {dev.OOBTerminalServerTCPPort}"
51
+
52
+ print(f"OOB Information for {dev.nodeName}:")
53
+ print(tn)
54
+ print("Connecting you now...")
55
+ os.system(tn)
56
+
57
+
58
+ def main():
59
+ """Main entry point for the CLI tool."""
60
+ global opts
61
+ opts, args = parse_args(sys.argv)
62
+
63
+ if os.getenv("DEBUG") is not None:
64
+ log.startLogging(sys.stdout, setStdout=False)
65
+
66
+ # Exception handling is done in device_match, returns None if no match.
67
+ dev = device_match(args[1].lower(), production_only=False)
68
+ if dev is None:
69
+ return 2
70
+
71
+ if dev.adminStatus != "PRODUCTION":
72
+ print("WARNING: You are connecting to a non-production device.")
73
+
74
+ if opts.oob:
75
+ connect_to_oob(dev)
76
+ return 0
77
+
78
+ # Connect to the device... and make sure to capture its exit code
79
+ retval = dev.connect()
80
+
81
+ print("\n") # Return cursor to beginning of line
82
+ return retval
83
+
84
+
85
+ if __name__ == "__main__":
86
+ sys.exit(main())