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/dicts.py ADDED
@@ -0,0 +1,357 @@
1
+ """
2
+ This code is originally from parser.py. It contains all the simple data definitions in
3
+ form of dictionaries and lists and such. This file is not meant to by used by itself.
4
+ Imported into support.py.
5
+
6
+ Variables defined:
7
+
8
+ adrsbk
9
+ dscp_names
10
+ fragment_flag_names
11
+ icmp_reject_codes
12
+ icmp_types
13
+ icmp_codes
14
+ ip_option_names
15
+ ios_icmp_messages
16
+ ios_icmp_names
17
+ junos_match_ordering_list
18
+ junos_match_order
19
+ address_matches
20
+ ports
21
+ precedence_names
22
+ tcp_flag_names
23
+ tcp_flag_specials
24
+ tcp_flag_rev
25
+ """
26
+
27
+ __author__ = "Jathan McCollum, Mike Biancaniello, Michael Harding, Michael Shields"
28
+ __editor__ = "Joseph Malone"
29
+ __maintainer__ = "Jathan McCollum"
30
+ __email__ = "jathanism@aol.com"
31
+ __copyright__ = "Copyright 2006-2013, AOL Inc.; 2013 Saleforce.com"
32
+
33
+ adrsbk = {"svc": {"group": {}, "book": {}}, "addr": {"group": {}, "book": {}}}
34
+
35
+ dscp_names = {
36
+ "be": 0,
37
+ "cs0": 0,
38
+ "cs1": 8,
39
+ "af11": 10,
40
+ "af12": 12,
41
+ "af13": 14,
42
+ "cs2": 16,
43
+ "af21": 18,
44
+ "af22": 20,
45
+ "af23": 22,
46
+ "cs3": 24,
47
+ "af31": 26,
48
+ "af32": 28,
49
+ "af33": 30,
50
+ "cs4": 32,
51
+ "af41": 34,
52
+ "af42": 36,
53
+ "af43": 38,
54
+ "cs5": 40,
55
+ "ef": 46,
56
+ "cs6": 48,
57
+ "cs7": 56,
58
+ }
59
+
60
+ fragment_flag_names = {
61
+ "dont-fragment": 0x4000,
62
+ "more-fragments": 0x2000,
63
+ "reserved": 0x8000,
64
+ }
65
+
66
+ icmp_reject_codes = (
67
+ "administratively-prohibited",
68
+ "bad-host-tos",
69
+ "bad-network-tos",
70
+ "host-prohibited",
71
+ "host-unknown",
72
+ "host-unreachable",
73
+ "network-prohibited",
74
+ "network-unknown",
75
+ "network-unreachable",
76
+ "port-unreachable",
77
+ "precedence-cutoff",
78
+ "precedence-violation",
79
+ "protocol-unreachable",
80
+ "source-host-isolated",
81
+ "source-route-failed",
82
+ "tcp-reset",
83
+ )
84
+
85
+ icmp_types = {
86
+ "echo-reply": 0,
87
+ "echo-request": 8,
88
+ "echo": 8, # undocumented
89
+ "info-reply": 16,
90
+ "info-request": 15,
91
+ "information-reply": 16,
92
+ "information-request": 15,
93
+ "mask-request": 17,
94
+ "mask-reply": 18,
95
+ "parameter-problem": 12,
96
+ "redirect": 5,
97
+ "router-advertisement": 9,
98
+ "router-solicit": 10,
99
+ "source-quench": 4,
100
+ "time-exceeded": 11,
101
+ "timestamp": 13,
102
+ "timestamp-reply": 14,
103
+ "unreachable": 3,
104
+ }
105
+
106
+ icmp_codes = {
107
+ "ip-header-bad": 0,
108
+ "required-option-missing": 1,
109
+ "redirect-for-host": 1,
110
+ "redirect-for-network": 0,
111
+ "redirect-for-tos-and-host": 3,
112
+ "redirect-for-tos-and-net": 2,
113
+ "ttl-eq-zero-during-reassembly": 1,
114
+ "ttl-eq-zero-during-transit": 0,
115
+ "communication-prohibited-by-filtering": 13,
116
+ "destination-host-prohibited": 10,
117
+ "destination-host-unknown": 7,
118
+ "destination-network-prohibited": 9,
119
+ "destination-network-unknown": 6,
120
+ "fragmentation-needed": 4,
121
+ "host-precedence-violation": 14,
122
+ "host-unreachable": 1,
123
+ "host-unreachable-for-TOS": 12,
124
+ "network-unreachable": 0,
125
+ "network-unreachable-for-TOS": 11,
126
+ "port-unreachable": 3,
127
+ "precedence-cutoff-in-effect": 15,
128
+ "protocol-unreachable": 2,
129
+ "source-host-isolated": 8,
130
+ "source-route-failed": 5,
131
+ }
132
+
133
+ ip_option_names = {
134
+ "loose-source-route": 131,
135
+ "record-route": 7,
136
+ "router-alert": 148,
137
+ "strict-source-route": 137,
138
+ "timestamp": 68,
139
+ }
140
+
141
+ # Cisco "ICMP message type names and ICMP message type and code names" from
142
+ # IOS 12.0 documentation. Verified these against actual practice of 12.1(21),
143
+ # since documentation is incomplete. For example, is 'echo' code 8, type 0
144
+ # or code 8, type any? Experiment shows that it is code 8, type any.
145
+ ios_icmp_messages = {
146
+ "administratively-prohibited": (3, 13),
147
+ "alternate-address": (6,),
148
+ "conversion-error": (31,),
149
+ "dod-host-prohibited": (3, 10),
150
+ "dod-net-prohibited": (3, 9),
151
+ "echo": (8,),
152
+ "echo-reply": (0,),
153
+ "general-parameter-problem": (12, 0),
154
+ "host-isolated": (3, 8),
155
+ "host-precedence-unreachable": (3, 14),
156
+ "host-redirect": (5, 1),
157
+ "host-tos-redirect": (5, 3),
158
+ "host-tos-unreachable": (3, 12),
159
+ "host-unknown": (3, 7),
160
+ "host-unreachable": (3, 1),
161
+ "information-reply": (16,),
162
+ "information-request": (15,),
163
+ "mask-reply": (18,),
164
+ "mask-request": (17,),
165
+ "mobile-redirect": (32,),
166
+ "net-redirect": (5, 0),
167
+ "net-tos-redirect": (5, 2),
168
+ "net-tos-unreachable": (3, 11),
169
+ "net-unreachable": (3, 0),
170
+ "network-unknown": (3, 6),
171
+ "no-room-for-option": (12, 2),
172
+ "option-missing": (12, 1),
173
+ "packet-too-big": (3, 4),
174
+ "parameter-problem": (12,),
175
+ "port-unreachable": (3, 3),
176
+ "precedence-unreachable": (3, 14), # not (3, 15)
177
+ "protocol-unreachable": (3, 2),
178
+ "reassembly-timeout": (11, 1), # not (11, 2)
179
+ "redirect": (5,),
180
+ "router-advertisement": (9,),
181
+ "router-solicitation": (10,),
182
+ "source-quench": (4,),
183
+ "source-route-failed": (3, 5),
184
+ "time-exceeded": (11,),
185
+ "timestamp-reply": (14,),
186
+ "timestamp-request": (13,),
187
+ "traceroute": (30,),
188
+ "ttl-exceeded": (11, 0),
189
+ "unreachable": (3,),
190
+ }
191
+
192
+ ios_icmp_names = {v: k for k, v in ios_icmp_messages.items()}
193
+
194
+ # Ordering for JunOS match clauses. AOL style rules:
195
+ # 1. Use the order found in the IP header, except, put protocol at the end
196
+ # so it is close to the port and tcp-flags.
197
+ # 2. General before specific.
198
+ # 3. Source before destination.
199
+ junos_match_ordering_list = (
200
+ "source-mac-address",
201
+ "destination-mac-address",
202
+ "packet-length",
203
+ "fragment-flags",
204
+ "fragment-offset",
205
+ "first-fragment",
206
+ "is-fragment",
207
+ "prefix-list",
208
+ "address",
209
+ "source-prefix-list",
210
+ "source-address",
211
+ "destination-prefix-list",
212
+ "destination-address",
213
+ "ip-options",
214
+ "protocol",
215
+ # TCP/UDP
216
+ "tcp-flags",
217
+ "port",
218
+ "source-port",
219
+ "destination-port",
220
+ # ICMP
221
+ "icmp-code",
222
+ "icmp-type",
223
+ )
224
+
225
+ junos_match_order = {}
226
+
227
+ for i, match in enumerate(junos_match_ordering_list):
228
+ junos_match_order[match] = i * 2
229
+ junos_match_order[match + "-except"] = i * 2 + 1
230
+
231
+ # These types of Juniper matches go in braces, not square brackets.
232
+ address_matches = {
233
+ "address",
234
+ "destination-address",
235
+ "source-address",
236
+ "prefix-list",
237
+ "source-prefix-list",
238
+ "destination-prefix-list",
239
+ }
240
+
241
+ for match in list(address_matches):
242
+ address_matches.add(match + "-except")
243
+
244
+ # Not all of these are in /etc/services even as of RHEL 4; for example, it
245
+ # has 'syslog' only in UDP, and 'dns' as 'domain'. Also, Cisco (according
246
+ # to the IOS 12.0 documentation at least) allows 'dns' in UDP and not TCP,
247
+ # along with other anomalies. We'll be somewhat more generous in parsing
248
+ # input, and always output as integers.
249
+ ports = {
250
+ "afs": 1483, # JunOS
251
+ "bgp": 179,
252
+ "biff": 512,
253
+ "bootpc": 68,
254
+ "bootps": 67,
255
+ "chargen": 19,
256
+ "cmd": 514, # undocumented IOS
257
+ "cvspserver": 2401, # JunOS
258
+ "daytime": 13,
259
+ "dhcp": 67, # JunOS
260
+ "discard": 9,
261
+ "dns": 53,
262
+ "dnsix": 90,
263
+ "domain": 53,
264
+ "echo": 7,
265
+ "eklogin": 2105, # JunOS
266
+ "ekshell": 2106, # JunOS
267
+ "exec": 512, # undocumented IOS
268
+ "finger": 79,
269
+ "ftp": 21,
270
+ "ftp-data": 20,
271
+ "gopher": 70,
272
+ "hostname": 101,
273
+ "http": 80, # JunOS
274
+ "https": 443, # JunOS
275
+ "ident": 113, # undocumented IOS
276
+ "imap": 143, # JunOS
277
+ "irc": 194,
278
+ "isakmp": 500, # undocumented IOS
279
+ "kerberos-sec": 88, # JunOS
280
+ "klogin": 543,
281
+ "kpasswd": 761, # JunOS
282
+ "kshell": 544,
283
+ "ldap": 389, # JunOS
284
+ "ldp": 646, # undocumented JunOS
285
+ "login": 513, # JunOS
286
+ "lpd": 515,
287
+ "mobile-ip": 434,
288
+ "mobileip-agent": 434, # JunOS
289
+ "mobileip-mn": 435, # JunOS
290
+ "msdp": 639, # JunOS
291
+ "nameserver": 42,
292
+ "netbios-dgm": 138,
293
+ "netbios-ns": 137,
294
+ "netbios-ssn": 139, # JunOS
295
+ "nfsd": 2049, # JunOS
296
+ "nntp": 119,
297
+ "ntalk": 518, # JunOS
298
+ "ntp": 123,
299
+ "pop2": 109,
300
+ "pop3": 110,
301
+ "pptp": 1723, # JunOS
302
+ "printer": 515, # JunOS
303
+ "radacct": 1813, # JunOS
304
+ "radius": 1812, # JunOS and undocumented IOS
305
+ "rip": 520,
306
+ "rkinit": 2108, # JunOS
307
+ "smtp": 25,
308
+ "snmp": 161,
309
+ "snmptrap": 162,
310
+ "snmp-trap": 162, # undocumented IOS
311
+ "snpp": 444, # JunOS
312
+ "socks": 1080, # JunOS
313
+ "ssh": 22, # JunOS
314
+ "sunrpc": 111,
315
+ "syslog": 514,
316
+ "tacacs": 49, # undocumented IOS
317
+ "tacacs-ds": 49,
318
+ "talk": 517,
319
+ "telnet": 23,
320
+ "tftp": 69,
321
+ "time": 37,
322
+ "timed": 525, # JunOS
323
+ "uucp": 540,
324
+ "who": 513,
325
+ "whois": 43,
326
+ "www": 80,
327
+ "xdmcp": 177,
328
+ "zephyr-clt": 2103, # JunOS
329
+ "zephyr-hm": 2104, # JunOS
330
+ }
331
+
332
+ precedence_names = {
333
+ "critical-ecp": 0xA0, # JunOS
334
+ "critical": 0xA0, # IOS
335
+ "flash": 0x60,
336
+ "flash-override": 0x80,
337
+ "immediate": 0x40,
338
+ "internet-control": 0xC0, # JunOS
339
+ "internet": 0xC0, # IOS
340
+ "net-control": 0xE0, # JunOS
341
+ "network": 0xE0, # IOS
342
+ "priority": 0x20,
343
+ "routine": 0x00,
344
+ }
345
+
346
+ tcp_flag_names = {
347
+ "ack": 0x10,
348
+ "fin": 0x01,
349
+ "push": 0x08,
350
+ "rst": 0x04,
351
+ "syn": 0x02,
352
+ "urgent": 0x20,
353
+ }
354
+
355
+ tcp_flag_specials = {"tcp-established": '"ack | rst"', "tcp-initial": '"syn & !ack"'}
356
+
357
+ tcp_flag_rev = {v: k for k, v in tcp_flag_specials.items()}
trigger/acl/grammar.py ADDED
@@ -0,0 +1,112 @@
1
+ """
2
+ This code is originally from parser.py. This is the basic grammar and rules
3
+ from which the other specific grammars are built. This file is not meant to by used by itself.
4
+ Imported into the specific grammar files.
5
+
6
+ #Constants
7
+ errs
8
+ rules
9
+ #Functions
10
+ S
11
+ literals
12
+ update
13
+ dict_sum
14
+ """
15
+
16
+ __author__ = "Jathan McCollum, Mike Biancaniello, Michael Harding, Michael Shields"
17
+ __editor__ = "Joseph Malone"
18
+ __maintainer__ = "Jathan McCollum"
19
+ __email__ = "jathanism@aol.com"
20
+ __copyright__ = "Copyright 2006-2013, AOL Inc.; 2013 Saleforce.com"
21
+
22
+ from .support import *
23
+
24
+ # Each production can be any of:
25
+ # 1. string
26
+ # if no subtags: -> matched text
27
+ # if single subtag: -> value of that
28
+ # if list: -> list of the value of each tag
29
+ # 2. (string, object) -> object
30
+ # 3. (string, callable_object) -> object(arg)
31
+
32
+ subtagged = set()
33
+
34
+
35
+ def S(prod):
36
+ """
37
+ Wrap your grammar token in this to call your helper function with a list
38
+ of each parsed subtag, instead of the raw text. This is useful for
39
+ performing modifiers.
40
+
41
+ :param prod: The parser product.
42
+ """
43
+ subtagged.add(prod)
44
+ return prod
45
+
46
+
47
+ def literals(d):
48
+ """Longest match of all the strings that are keys of 'd'."""
49
+ keys = [str(key) for key in d]
50
+ keys.sort(key=lambda x: -len(x)) # Sort by length descending
51
+ return " / ".join(['"%s"' % key for key in keys])
52
+
53
+
54
+ def update(d, **kwargs):
55
+ # Check for duplicate subterms, which is legal but too confusing to be
56
+ # allowed at AOL. For example, a Juniper term can have two different
57
+ # 'destination-address' clauses, which means that the first will be
58
+ # ignored. This led to an outage on 2006-10-11.
59
+ for key in kwargs.keys():
60
+ if key in d:
61
+ raise exceptions.ParseError("duplicate %s" % key)
62
+ d.update(kwargs)
63
+ return d
64
+
65
+
66
+ def dict_sum(dlist):
67
+ dsum = {}
68
+ for d in dlist:
69
+ for k, v in d.items():
70
+ if k in dsum:
71
+ dsum[k] += v
72
+ else:
73
+ dsum[k] = v
74
+ return dsum
75
+
76
+
77
+ ## syntax error messages
78
+ errs = {
79
+ "comm_start": '"comment missing /* below line %(line)s"',
80
+ "comm_stop": '"comment missing */ below line %(line)s"',
81
+ "default": '"expected %(expected)s line %(line)s"',
82
+ "semicolon": '"missing semicolon on line %(line)s"',
83
+ }
84
+
85
+ rules = {
86
+ "digits": "[0-9]+",
87
+ "<digits_s>": "[0-9]+",
88
+ "<ts>": "[ \\t]+",
89
+ "<ws>": "[ \\t\\n]+",
90
+ "<EOL>": "('\r'?,'\n')/EOF",
91
+ "alphanums": "[a-zA-Z0-9]+",
92
+ "word": "[a-zA-Z0-9_.-]+",
93
+ "anychar": "[ a-zA-Z0-9.$:()&,/'_-]",
94
+ "hex": "[0-9a-fA-F]+",
95
+ "ipchars": "[0-9a-fA-F:.]+",
96
+ "ipv4": ('digits, (".", digits)*', TIP),
97
+ "ipaddr": ("ipchars", TIP),
98
+ "cidr": (
99
+ '("inactive:", ws+)?, (ipaddr / ipv4), "/", digits, (ws+, "except")?',
100
+ TIP,
101
+ ),
102
+ "macaddr": 'hex, (":", hex)+',
103
+ "protocol": (literals(Protocol.name2num) + " / digits", do_protocol_lookup),
104
+ "tcp": ('"tcp" / "6"', Protocol("tcp")),
105
+ "udp": ('"udp" / "17"', Protocol("udp")),
106
+ "icmp": ('"icmp" / "1"', Protocol("icmp")),
107
+ "icmp_type": (literals(icmp_types) + " / digits", do_icmp_type_lookup),
108
+ "icmp_code": (literals(icmp_codes) + " / digits", do_icmp_code_lookup),
109
+ "port": (literals(ports) + " / digits", do_port_lookup),
110
+ "dscp": (literals(dscp_names) + " / digits", do_dscp_lookup),
111
+ "root": "ws?, junos_raw_acl / junos_replace_family_acl / junos_replace_acl / junos_replace_policers / ios_acl, ws?",
112
+ }
trigger/acl/ios.py ADDED
@@ -0,0 +1,222 @@
1
+ """
2
+ This code is originally from parser.py. This file is not meant to by used by itself.
3
+ This is for IOS like ACLs
4
+
5
+ #Constants
6
+ unary_port_operators
7
+ rules - this is really from the grammar.py
8
+ # Classes
9
+ 'Remark',
10
+ # Functions
11
+ 'handle_ios_match',
12
+ 'handle_ios_acl',
13
+ """
14
+
15
+ # Copied metadata from parser.py
16
+ __author__ = "Jathan McCollum, Mike Biancaniello, Michael Harding, Michael Shields"
17
+ __editor__ = "Joseph Malone"
18
+ __maintainer__ = "Jathan McCollum"
19
+ __email__ = "jathanism@aol.com"
20
+ __copyright__ = "Copyright 2006-2013, AOL Inc.; 2013 Saleforce.com"
21
+
22
+ from .grammar import *
23
+
24
+
25
+ class Remark(Comment):
26
+ """
27
+ IOS extended ACL "remark" lines automatically become comments when
28
+ converting to other formats of ACL.
29
+ """
30
+
31
+ def output_ios_named(self):
32
+ """Output the Remark to IOS named format."""
33
+ return " remark " + self.data
34
+
35
+
36
+ # Build a table to unwind Cisco's weird inverse netmask.
37
+ # TODO (jathan): These don't actually get sorted properly, but it doesn't seem
38
+ # to have mattered up until now. Worth looking into it at some point, though.
39
+ inverse_mask_table = dict([(make_inverse_mask(x), x) for x in range(0, 33)])
40
+
41
+
42
+ def handle_ios_match(a):
43
+ protocol, source, dest = a[:3]
44
+ extra = a[3:]
45
+
46
+ match = Matches()
47
+ modifiers = Modifiers()
48
+
49
+ if protocol:
50
+ match["protocol"] = [protocol]
51
+
52
+ for sd, arg in (("source", source), ("destination", dest)):
53
+ if isinstance(arg, list):
54
+ if arg[0] is not None:
55
+ match[sd + "-address"] = [arg[0]]
56
+ match[sd + "-port"] = arg[1]
57
+ else:
58
+ if arg is not None:
59
+ match[sd + "-address"] = [arg]
60
+
61
+ if "log" in extra:
62
+ modifiers["syslog"] = True
63
+ extra.remove("log")
64
+
65
+ if protocol == "icmp":
66
+ if len(extra) > 2:
67
+ raise NotImplementedError(extra)
68
+ if extra and isinstance(extra[0], tuple):
69
+ extra = extra[0]
70
+ if len(extra) >= 1:
71
+ match["icmp-type"] = [extra[0]]
72
+ if len(extra) >= 2:
73
+ match["icmp-code"] = [extra[1]]
74
+ elif protocol == "tcp":
75
+ if extra == ["established"]:
76
+ match["tcp-flags"] = [tcp_flag_specials["tcp-established"]]
77
+ elif extra:
78
+ raise NotImplementedError(extra)
79
+ elif extra:
80
+ raise NotImplementedError(extra)
81
+
82
+ return {"match": match, "modifiers": modifiers}
83
+
84
+
85
+ def handle_ios_acl(rows):
86
+ acl = ACL()
87
+ for d in rows:
88
+ if not d:
89
+ continue
90
+ for k, v in d.items():
91
+ if k == "no":
92
+ acl = ACL()
93
+ elif k == "name":
94
+ if acl.name:
95
+ if v != acl.name:
96
+ raise exceptions.ACLNameError(
97
+ f"Name '{v}' does not match ACL '{acl.name}'"
98
+ )
99
+ else:
100
+ acl.name = v
101
+ elif k == "term":
102
+ acl.terms.append(v)
103
+ elif k == "format":
104
+ acl.format = v
105
+ # Brocade receive-acl
106
+ elif k == "receive_acl":
107
+ acl.is_receive_acl = True
108
+ else:
109
+ raise RuntimeError(f'unknown key "{k}" (value {v})')
110
+ # In traditional ACLs, comments that belong to the first ACE are
111
+ # indistinguishable from comments that belong to the ACL.
112
+ # if acl.format == 'ios' and acl.terms:
113
+ if acl.format in ("ios", "ios_brocade") and acl.terms:
114
+ acl.comments += acl.terms[0].comments
115
+ acl.terms[0].comments = []
116
+ return acl
117
+
118
+
119
+ unary_port_operators = {
120
+ "eq": lambda x: [x],
121
+ "le": lambda x: [(0, x)],
122
+ "lt": lambda x: [(0, x - 1)],
123
+ "ge": lambda x: [(x, 65535)],
124
+ "gt": lambda x: [(x + 1, 65535)],
125
+ "neq": lambda x: [(0, x - 1), (x + 1, 65535)],
126
+ }
127
+
128
+ rules.update(
129
+ {
130
+ "ios_ip": "kw_any / host_ipv4 / ios_masked_ipv4",
131
+ "kw_any": ('"any"', None),
132
+ "host_ipv4": '"host", ts, ipv4',
133
+ S("ios_masked_ipv4"): (
134
+ "ipv4, ts, ipv4_inverse_mask",
135
+ # Python 3: Explicit tuple unpacking for string formatting
136
+ lambda x: TIP("%s/%d" % (x[0], x[1])),
137
+ ),
138
+ "ipv4_inverse_mask": (
139
+ literals(inverse_mask_table),
140
+ lambda x: inverse_mask_table[TIP(x)],
141
+ ),
142
+ "kw_ip": ('"ip"', None),
143
+ S("ios_match"): (
144
+ "kw_ip / protocol, ts, ios_ip, ts, ios_ip, (ts, ios_log)?",
145
+ handle_ios_match,
146
+ ),
147
+ S("ios_tcp_port_match"): (
148
+ "tcp, ts, ios_ip_port, ts, ios_ip_port, (ts, established)?, (ts, ios_log)?",
149
+ handle_ios_match,
150
+ ),
151
+ S("ios_udp_port_match"): (
152
+ "udp, ts, ios_ip_port, ts, ios_ip_port, (ts, ios_log)?",
153
+ handle_ios_match,
154
+ ),
155
+ S("ios_ip_port"): "ios_ip, (ts, unary_port / ios_range)?",
156
+ S("unary_port"): (
157
+ "unary_port_operator, ts, port",
158
+ lambda x: unary_port_operators[x[0]](x[1]),
159
+ ),
160
+ "unary_port_operator": literals(unary_port_operators),
161
+ S("ios_range"): ('"range", ts, port, ts, port', lambda xy: [(xy[0], xy[1])]),
162
+ "established": '"established"',
163
+ S("ios_icmp_match"): (
164
+ "icmp, ts, ios_ip, ts, ios_ip, (ts, ios_log)?, "
165
+ "(ts, ios_icmp_message / "
166
+ " (icmp_type, (ts, icmp_code)?))?, (ts, ios_log)?",
167
+ handle_ios_match,
168
+ ),
169
+ "ios_icmp_message": (
170
+ literals(ios_icmp_messages),
171
+ lambda x: ios_icmp_messages[x],
172
+ ),
173
+ "ios_action": '"permit" / "deny"',
174
+ "ios_log": '"log-input" / "log"',
175
+ S("ios_action_match"): (
176
+ "ios_action, ts, ios_tcp_port_match / "
177
+ "ios_udp_port_match / ios_icmp_match / ios_match",
178
+ lambda x: {"term": Term(action=x[0], **x[1])},
179
+ ),
180
+ "ios_acl_line": "ios_acl_match_line / ios_acl_no_line",
181
+ S("ios_acl_match_line"): (
182
+ '"access-list", ts, digits, ts, ios_action_match',
183
+ lambda x: update(x[1], name=x[0], format="ios"),
184
+ ),
185
+ S("ios_acl_no_line"): (
186
+ '"no", ts, "access-list", ts, digits',
187
+ lambda x: {"no": True, "name": x[0]},
188
+ ),
189
+ "ios_ext_line": (
190
+ "ios_action_match / ios_ext_name_line / "
191
+ "ios_ext_no_line / ios_remark_line / "
192
+ "ios_rebind_acl_line / ios_rebind_receive_acl_line"
193
+ ),
194
+ S("ios_ext_name_line"): (
195
+ '"ip", ts, "access-list", ts, "extended", ts, word',
196
+ lambda x: {"name": x[0], "format": "ios_named"},
197
+ ),
198
+ S("ios_ext_no_line"): (
199
+ '"no", ts, "ip", ts, "access-list", ts, "extended", ts, word',
200
+ lambda x: {"no": True, "name": x[0]},
201
+ ),
202
+ # Brocade "ip rebind-acl foo" or "ip rebind-receive-acl foo" syntax
203
+ S("ios_rebind_acl_line"): (
204
+ '"ip", ts, "rebind-acl", ts, word',
205
+ lambda x: {"name": x[0], "format": "ios_brocade"},
206
+ ),
207
+ # Brocade "ip rebind-acl foo" or "ip rebind-receive-acl foo" syntax
208
+ S("ios_rebind_receive_acl_line"): (
209
+ '"ip", ts, "rebind-receive-acl", ts, word',
210
+ lambda x: {"name": x[0], "format": "ios_brocade", "receive_acl": True},
211
+ ),
212
+ S("icomment"): ('"!", ts?, icomment_body', lambda x: x),
213
+ "icomment_body": ('-"\n"*', Comment),
214
+ S("ios_remark_line"): (
215
+ '("access-list", ts, digits_s, ts)?, "remark", ts, remark_body',
216
+ lambda x: x,
217
+ ),
218
+ "remark_body": ('-"\n"*', Remark),
219
+ ">ios_line<": ('ts?, (ios_acl_line / ios_ext_line / "end")?, ts?, icomment?'),
220
+ S("ios_acl"): ('(ios_line, "\n")*, ios_line', handle_ios_acl),
221
+ }
222
+ )