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.
- trigger/__init__.py +7 -0
- trigger/acl/__init__.py +32 -0
- trigger/acl/autoacl.py +70 -0
- trigger/acl/db.py +324 -0
- trigger/acl/dicts.py +357 -0
- trigger/acl/grammar.py +112 -0
- trigger/acl/ios.py +222 -0
- trigger/acl/junos.py +422 -0
- trigger/acl/models.py +118 -0
- trigger/acl/parser.py +168 -0
- trigger/acl/queue.py +296 -0
- trigger/acl/support.py +1431 -0
- trigger/acl/tools.py +746 -0
- trigger/bin/__init__.py +0 -0
- trigger/bin/acl.py +233 -0
- trigger/bin/acl_script.py +574 -0
- trigger/bin/aclconv.py +82 -0
- trigger/bin/check_access.py +93 -0
- trigger/bin/check_syntax.py +66 -0
- trigger/bin/fe.py +197 -0
- trigger/bin/find_access.py +191 -0
- trigger/bin/gnng.py +434 -0
- trigger/bin/gong.py +86 -0
- trigger/bin/load_acl.py +841 -0
- trigger/bin/load_config.py +18 -0
- trigger/bin/netdev.py +317 -0
- trigger/bin/optimizer.py +638 -0
- trigger/bin/run_cmds.py +18 -0
- trigger/changemgmt/__init__.py +352 -0
- trigger/changemgmt/bounce.py +57 -0
- trigger/cmds.py +1217 -0
- trigger/conf/__init__.py +94 -0
- trigger/conf/global_settings.py +674 -0
- trigger/contrib/__init__.py +7 -0
- trigger/exceptions.py +307 -0
- trigger/gorc.py +172 -0
- trigger/netdevices/__init__.py +1288 -0
- trigger/netdevices/loader.py +174 -0
- trigger/netscreen.py +1030 -0
- trigger/packages/__init__.py +6 -0
- trigger/packages/peewee.py +8084 -0
- trigger/rancid.py +463 -0
- trigger/tacacsrc.py +584 -0
- trigger/twister.py +2203 -0
- trigger/twister2.py +745 -0
- trigger/utils/__init__.py +88 -0
- trigger/utils/cli.py +349 -0
- trigger/utils/importlib.py +77 -0
- trigger/utils/network.py +157 -0
- trigger/utils/rcs.py +178 -0
- trigger/utils/templates.py +81 -0
- trigger/utils/url.py +78 -0
- trigger/utils/xmltodict.py +298 -0
- trigger-2.0.0.dist-info/METADATA +146 -0
- trigger-2.0.0.dist-info/RECORD +61 -0
- trigger-2.0.0.dist-info/WHEEL +5 -0
- trigger-2.0.0.dist-info/entry_points.txt +15 -0
- trigger-2.0.0.dist-info/licenses/AUTHORS.md +20 -0
- trigger-2.0.0.dist-info/licenses/LICENSE.md +28 -0
- trigger-2.0.0.dist-info/top_level.txt +2 -0
- twisted/plugins/trigger_xmlrpc.py +124 -0
trigger/acl/junos.py
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This code is originally from parser.py. This file is not meant to by used by itself.
|
|
3
|
+
This is for JunOS like ACLs
|
|
4
|
+
|
|
5
|
+
#Constants
|
|
6
|
+
junos_match_types
|
|
7
|
+
rules - this is really from the grammar.py
|
|
8
|
+
# Classes
|
|
9
|
+
Policer
|
|
10
|
+
PolicerGroup
|
|
11
|
+
QuotedString
|
|
12
|
+
# Functions
|
|
13
|
+
braced_list
|
|
14
|
+
keyword_match
|
|
15
|
+
range_match
|
|
16
|
+
handle_junos_acl
|
|
17
|
+
handle_junos_family_acl
|
|
18
|
+
handle_junos_policers
|
|
19
|
+
handle_junos_term
|
|
20
|
+
juniper_multiline_comments
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# Copied metadata from parser.py
|
|
24
|
+
__author__ = "Jathan McCollum, Mike Biancaniello, Michael Harding, Michael Shields"
|
|
25
|
+
__editor__ = "Joseph Malone"
|
|
26
|
+
__maintainer__ = "Jathan McCollum"
|
|
27
|
+
__email__ = "jathanism@aol.com"
|
|
28
|
+
__copyright__ = "Copyright 2006-2013, AOL Inc.; 2013 Saleforce.com"
|
|
29
|
+
|
|
30
|
+
from trigger.conf import settings
|
|
31
|
+
|
|
32
|
+
from .grammar import *
|
|
33
|
+
|
|
34
|
+
# Temporary resting place for comments, so the rest of the parser can
|
|
35
|
+
# ignore them. Yes, this makes the library not thread-safe.
|
|
36
|
+
Comments = []
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Policer:
|
|
40
|
+
"""
|
|
41
|
+
Container class for policer policy definitions. This is a dummy class for
|
|
42
|
+
now, that just passes it through as a string.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, name, data):
|
|
46
|
+
if not name:
|
|
47
|
+
raise exceptions.ActionError("Policer requres name")
|
|
48
|
+
self.name = name
|
|
49
|
+
self.exceedings = []
|
|
50
|
+
self.actions = []
|
|
51
|
+
for elt in data:
|
|
52
|
+
for k, v in elt.items():
|
|
53
|
+
if k == "if-exceeding":
|
|
54
|
+
for entry in v:
|
|
55
|
+
type, value = entry
|
|
56
|
+
if type == "bandwidth-limit":
|
|
57
|
+
limit = self.str2bits(value)
|
|
58
|
+
if limit > 32000000000 or limit < 32000:
|
|
59
|
+
raise "bandwidth-limit must be between 32000bps and 32000000000bps"
|
|
60
|
+
self.exceedings.append((type, limit))
|
|
61
|
+
elif type == "burst-size-limit":
|
|
62
|
+
limit = self.str2bits(value)
|
|
63
|
+
if limit > 100000000 or limit < 1500:
|
|
64
|
+
raise "burst-size-limit must be between 1500B and 100,000,000B"
|
|
65
|
+
self.exceedings.append((type, limit))
|
|
66
|
+
elif type == "bandwidth-percent":
|
|
67
|
+
limit = int(value)
|
|
68
|
+
if limit < 1 or limit > 100:
|
|
69
|
+
raise "bandwidth-percent must be between 1 and 100"
|
|
70
|
+
else:
|
|
71
|
+
raise f"Unknown policer if-exceeding tag: {type}"
|
|
72
|
+
elif k == "action":
|
|
73
|
+
for i in v:
|
|
74
|
+
self.actions.append(i)
|
|
75
|
+
|
|
76
|
+
def str2bits(self, str):
|
|
77
|
+
try:
|
|
78
|
+
val = int(str)
|
|
79
|
+
except:
|
|
80
|
+
if str[-1] == "k":
|
|
81
|
+
return int(str[0:-1]) * 1024
|
|
82
|
+
if str[-1] == "m":
|
|
83
|
+
return int(str[0:-1]) * 1048576
|
|
84
|
+
else:
|
|
85
|
+
raise f"invalid bit definition {str}"
|
|
86
|
+
return val
|
|
87
|
+
|
|
88
|
+
def __repr__(self):
|
|
89
|
+
return f"<{self.__class__.__name__}: {repr(self.name)}>"
|
|
90
|
+
|
|
91
|
+
def __str__(self):
|
|
92
|
+
return self.data
|
|
93
|
+
|
|
94
|
+
def output(self):
|
|
95
|
+
output = [f"policer {self.name} {{"]
|
|
96
|
+
if self.exceedings:
|
|
97
|
+
output.append(" if-exceeding {")
|
|
98
|
+
for x in self.exceedings:
|
|
99
|
+
output.append(f" {x[0]} {x[1]};")
|
|
100
|
+
if self.exceedings:
|
|
101
|
+
output.append(" }")
|
|
102
|
+
if self.actions:
|
|
103
|
+
output.append(" then {")
|
|
104
|
+
for x in self.actions:
|
|
105
|
+
output.append(f" {x};")
|
|
106
|
+
|
|
107
|
+
if self.actions:
|
|
108
|
+
output.append(" }")
|
|
109
|
+
output.append("}")
|
|
110
|
+
return output
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class PolicerGroup:
|
|
114
|
+
"""Container for Policer objects. Juniper only."""
|
|
115
|
+
|
|
116
|
+
def __init__(self, format=None):
|
|
117
|
+
self.policers = []
|
|
118
|
+
self.format = format
|
|
119
|
+
global Comments
|
|
120
|
+
self.comments = Comments
|
|
121
|
+
Comments = []
|
|
122
|
+
|
|
123
|
+
def output(self, format=None, *largs, **kwargs):
|
|
124
|
+
if format is None:
|
|
125
|
+
format = self.format
|
|
126
|
+
return getattr(self, "output_" + format)(*largs, **kwargs)
|
|
127
|
+
|
|
128
|
+
def output_junos(self, replace=False):
|
|
129
|
+
output = []
|
|
130
|
+
for ent in self.policers:
|
|
131
|
+
for x in ent.output():
|
|
132
|
+
output.append(x)
|
|
133
|
+
|
|
134
|
+
if replace:
|
|
135
|
+
return ["firewall {", "replace:"] + [" " + x for x in output] + ["}"]
|
|
136
|
+
else:
|
|
137
|
+
return output
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class QuotedString(str):
|
|
141
|
+
def __str__(self):
|
|
142
|
+
return '"' + self + '"'
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
junos_match_types = []
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def braced_list(arg):
|
|
149
|
+
"""Returned braced output. Will alert if comment is malformed."""
|
|
150
|
+
# return '("{", jws?, (%s, jws?)*, "}")' % arg
|
|
151
|
+
return '("{{", jws?, ({}, jws?)*, "}}"!{})'.format(arg, errs["comm_start"])
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def keyword_match(keyword, arg=None):
|
|
155
|
+
for k in keyword, keyword + "-except":
|
|
156
|
+
prod = "junos_" + k.replace("-", "_")
|
|
157
|
+
junos_match_types.append(prod)
|
|
158
|
+
if arg is None:
|
|
159
|
+
rules[prod] = (f'"{k}", jsemi', {k: True})
|
|
160
|
+
else:
|
|
161
|
+
tokens = f'"{k}", jws, '
|
|
162
|
+
if k in address_matches:
|
|
163
|
+
tokens += braced_list(arg + ", jsemi")
|
|
164
|
+
else:
|
|
165
|
+
tokens += arg + ", jsemi"
|
|
166
|
+
rules[S(prod)] = (tokens, lambda x, k=k: {k: x})
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
keyword_match("address", "cidr / ipaddr")
|
|
170
|
+
keyword_match("destination-address", "cidr / ipaddr")
|
|
171
|
+
keyword_match("destination-prefix-list", "jword")
|
|
172
|
+
keyword_match("first-fragment")
|
|
173
|
+
keyword_match("fragment-flags", "fragment_flag")
|
|
174
|
+
keyword_match("ip-options", "ip_option")
|
|
175
|
+
keyword_match("is-fragment")
|
|
176
|
+
keyword_match("prefix-list", "jword")
|
|
177
|
+
keyword_match("source-address", "cidr / ipaddr")
|
|
178
|
+
keyword_match("source-prefix-list", "jword")
|
|
179
|
+
keyword_match("tcp-established")
|
|
180
|
+
keyword_match("tcp-flags", "tcp_flag")
|
|
181
|
+
keyword_match("tcp-initial")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def range_match(key, arg):
|
|
185
|
+
rules[S(arg + "_range")] = (f'{arg}, "-", {arg}', tuple)
|
|
186
|
+
match = f"{arg}_range / {arg}"
|
|
187
|
+
keyword_match(key, f'{match} / ("[", jws?, ({match}, jws?)*, "]")')
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
range_match("ah-spi", "alphanums")
|
|
191
|
+
range_match("destination-mac-address", "macaddr")
|
|
192
|
+
range_match("destination-port", "port")
|
|
193
|
+
range_match("dscp", "dscp")
|
|
194
|
+
range_match("ether-type", "alphanums")
|
|
195
|
+
range_match("esp-spi", "alphanums")
|
|
196
|
+
range_match("forwarding-class", "jword")
|
|
197
|
+
range_match("fragment-offset", "port")
|
|
198
|
+
range_match("icmp-code", "icmp_code")
|
|
199
|
+
range_match("icmp-type", "icmp_type")
|
|
200
|
+
range_match("interface-group", "digits")
|
|
201
|
+
range_match("packet-length", "digits")
|
|
202
|
+
range_match("port", "port")
|
|
203
|
+
range_match("precedence", "jword")
|
|
204
|
+
range_match("protocol", "protocol")
|
|
205
|
+
range_match("source-mac-address", "macaddr")
|
|
206
|
+
range_match("source-port", "port")
|
|
207
|
+
range_match("vlan-ether-type", "alphanums")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def handle_junos_acl(x):
|
|
211
|
+
"""
|
|
212
|
+
Parse JUNOS ACL and return an ACL object populated with Term and Policer
|
|
213
|
+
objects.
|
|
214
|
+
|
|
215
|
+
It's expected that x is a 2-tuple of (name, terms) returned from the
|
|
216
|
+
parser.
|
|
217
|
+
|
|
218
|
+
Don't forget to wrap your token in S()!
|
|
219
|
+
"""
|
|
220
|
+
a = ACL(name=x[0], format="junos")
|
|
221
|
+
for elt in x[1:]:
|
|
222
|
+
# Handle dictionary args we throw at the constructor
|
|
223
|
+
if isinstance(elt, dict):
|
|
224
|
+
a.__dict__.update(elt)
|
|
225
|
+
elif isinstance(elt, Term):
|
|
226
|
+
a.terms.append(elt)
|
|
227
|
+
elif isinstance(elt, Policer):
|
|
228
|
+
a.policers.append(elt)
|
|
229
|
+
else:
|
|
230
|
+
raise RuntimeError(f"Bad Object: {repr(elt)}")
|
|
231
|
+
return a
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def handle_junos_family_acl(x):
|
|
235
|
+
"""
|
|
236
|
+
Parses a JUNOS acl that contains family information and sets the family
|
|
237
|
+
attribute for the ACL object.
|
|
238
|
+
|
|
239
|
+
It's expected that x is a 2-tuple of (family, aclobj) returned from the
|
|
240
|
+
parser.
|
|
241
|
+
|
|
242
|
+
Don't forget to wrap your token in S()!
|
|
243
|
+
"""
|
|
244
|
+
family, aclobj = x
|
|
245
|
+
setattr(aclobj, "family", family)
|
|
246
|
+
return aclobj
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def handle_junos_policers(x):
|
|
250
|
+
"""Parse JUNOS policers and return a PolicerGroup object"""
|
|
251
|
+
p = PolicerGroup(format="junos")
|
|
252
|
+
for elt in x:
|
|
253
|
+
if isinstance(elt, Policer):
|
|
254
|
+
p.policers.append(elt)
|
|
255
|
+
else:
|
|
256
|
+
raise RuntimeError(f"bad object: {repr(elt)} in policer")
|
|
257
|
+
return p
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def handle_junos_term(d):
|
|
261
|
+
"""Parse a JUNOS term and return a Term object"""
|
|
262
|
+
if "modifiers" in d:
|
|
263
|
+
d["modifiers"] = Modifiers(d["modifiers"])
|
|
264
|
+
return Term(**d)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
# For multiline comments
|
|
268
|
+
def juniper_multiline_comments():
|
|
269
|
+
"""
|
|
270
|
+
Return appropriate multi-line comment grammar for Juniper ACLs.
|
|
271
|
+
|
|
272
|
+
This depends on ``settings.ALLOW_JUNIPER_MULTLIINE_COMMENTS``.
|
|
273
|
+
"""
|
|
274
|
+
single = '-("*/" / "\n")*' # single-line comments only
|
|
275
|
+
multi = '-"*/"*' # syntactically correct multi-line support
|
|
276
|
+
if settings.ALLOW_JUNIPER_MULTILINE_COMMENTS:
|
|
277
|
+
return multi
|
|
278
|
+
return single
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
rules.update(
|
|
282
|
+
{
|
|
283
|
+
"jword": "double_quoted / word",
|
|
284
|
+
"double_quoted": ('"\\"", -[\\"]+, "\\""', lambda x: QuotedString(x[1:-1])),
|
|
285
|
+
#'>jws<': '(ws / jcomment)+',
|
|
286
|
+
# S('jcomment'): ('"/*", ws?, jcomment_body, ws?, "*/"',
|
|
287
|
+
# lambda x: Comment(x[0])),
|
|
288
|
+
#'jcomment_body': '-(ws?, "*/")*',
|
|
289
|
+
">jws<": "(ws / jcomment)+",
|
|
290
|
+
S("jcomment"): ("jslashbang_comment", lambda x: Comment(x[0])),
|
|
291
|
+
"<comment_start>": '"/*"',
|
|
292
|
+
"<comment_stop>": '"*/"',
|
|
293
|
+
">jslashbang_comment<": "comment_start, jcomment_body, !{}, comment_stop".format(
|
|
294
|
+
errs["comm_stop"]
|
|
295
|
+
),
|
|
296
|
+
"jcomment_body": juniper_multiline_comments(),
|
|
297
|
+
# Errors on missing ';', ignores multiple ;; and normalizes to one.
|
|
298
|
+
"<jsemi>": "jws?, [;]+!{}".format(errs["semicolon"]),
|
|
299
|
+
"fragment_flag": literals(fragment_flag_names),
|
|
300
|
+
"ip_option": "digits / " + literals(ip_option_names),
|
|
301
|
+
"tcp_flag": literals(tcp_flag_names),
|
|
302
|
+
}
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# Note there cannot be jws (including comments) before or after the "filter"
|
|
306
|
+
# section of the config. It's wrong to do this anyway, since if you load
|
|
307
|
+
# that config onto the router, the comments will not remain in place on
|
|
308
|
+
# the next load of a similar config (e.g., another ACL). I had a workaround
|
|
309
|
+
# for this but it made the parser substantially slower.
|
|
310
|
+
rules.update(
|
|
311
|
+
{
|
|
312
|
+
S("junos_raw_acl"): (
|
|
313
|
+
'jws?, "filter", jws, jword, jws?, '
|
|
314
|
+
+ braced_list("junos_iface_specific / junos_term / junos_policer"),
|
|
315
|
+
handle_junos_acl,
|
|
316
|
+
),
|
|
317
|
+
"junos_iface_specific": (
|
|
318
|
+
'("interface-specific", jsemi)',
|
|
319
|
+
lambda x: {"interface_specific": len(x) > 0},
|
|
320
|
+
),
|
|
321
|
+
"junos_replace_acl": (
|
|
322
|
+
'jws?, "firewall", jws?, "{", jws?, "replace:", jws?, (junos_raw_acl, jws?)*, "}"'
|
|
323
|
+
),
|
|
324
|
+
S("junos_replace_family_acl"): (
|
|
325
|
+
'jws?, "firewall", jws?, "{", jws?, junos_filter_family, jws?, "{", jws?, "replace:", jws?, (junos_raw_acl, jws?)*, "}", jws?, "}"',
|
|
326
|
+
handle_junos_family_acl,
|
|
327
|
+
),
|
|
328
|
+
S("junos_replace_policers"): (
|
|
329
|
+
'"firewall", jws?, "{", jws?, "replace:", jws?, (junos_policer, jws?)*, "}"',
|
|
330
|
+
handle_junos_policers,
|
|
331
|
+
),
|
|
332
|
+
"junos_filter_family": ('"family", ws, junos_family_type'),
|
|
333
|
+
"junos_family_type": ('"inet" / "inet6" / "ethernet-switching"'),
|
|
334
|
+
"opaque_braced_group": (
|
|
335
|
+
'"{", jws?, (jword / "[" / "]" / ";" / opaque_braced_group / jws)*, "}"',
|
|
336
|
+
lambda x: x,
|
|
337
|
+
),
|
|
338
|
+
S("junos_term"): (
|
|
339
|
+
'maybe_inactive, "term", jws, junos_term_name, '
|
|
340
|
+
"jws?, " + braced_list("junos_from / junos_then"),
|
|
341
|
+
lambda x: handle_junos_term(dict_sum(x)),
|
|
342
|
+
),
|
|
343
|
+
S("junos_term_name"): ("jword", lambda x: {"name": x[0]}),
|
|
344
|
+
"maybe_inactive": ('("inactive:", jws)?', lambda x: {"inactive": len(x) > 0}),
|
|
345
|
+
S("junos_from"): (
|
|
346
|
+
'"from", jws?, ' + braced_list("junos_match"),
|
|
347
|
+
lambda x: {"match": Matches(dict_sum(x))},
|
|
348
|
+
),
|
|
349
|
+
S("junos_then"): ("junos_basic_then / junos_braced_then", dict_sum),
|
|
350
|
+
S("junos_braced_then"): (
|
|
351
|
+
'"then", jws?, ' + braced_list("junos_action/junos_modifier, jsemi"),
|
|
352
|
+
dict_sum,
|
|
353
|
+
),
|
|
354
|
+
S("junos_basic_then"): ('"then", jws?, junos_action, jsemi', dict_sum),
|
|
355
|
+
S("junos_policer"): (
|
|
356
|
+
'"policer", jws, junos_term_name, jws?, '
|
|
357
|
+
+ braced_list("junos_exceeding / junos_policer_then"),
|
|
358
|
+
lambda x: Policer(x[0]["name"], x[1:]),
|
|
359
|
+
),
|
|
360
|
+
S("junos_policer_then"): (
|
|
361
|
+
'"then", jws?, ' + braced_list("junos_policer_action, jsemi")
|
|
362
|
+
),
|
|
363
|
+
S("junos_policer_action"): (
|
|
364
|
+
'junos_discard / junos_fwd_class / ("loss-priority", jws, jword)',
|
|
365
|
+
lambda x: {"action": x},
|
|
366
|
+
),
|
|
367
|
+
"junos_discard": ('"discard"'),
|
|
368
|
+
"junos_loss_pri": (
|
|
369
|
+
'"loss-priority", jws, jword',
|
|
370
|
+
lambda x: {"loss-priority": x[0]},
|
|
371
|
+
),
|
|
372
|
+
"junos_fwd_class": (
|
|
373
|
+
'"forwarding-class", jws, jword',
|
|
374
|
+
lambda x: {"forwarding-class": x[0]},
|
|
375
|
+
),
|
|
376
|
+
"junos_filter_specific": ('"filter-specific"'),
|
|
377
|
+
S("junos_exceeding"): (
|
|
378
|
+
'"if-exceeding", jws?, '
|
|
379
|
+
+ braced_list("junos_bw_limit/junos_bw_perc/junos_burst_limit"),
|
|
380
|
+
lambda x: {"if-exceeding": x},
|
|
381
|
+
),
|
|
382
|
+
S("junos_bw_limit"): (
|
|
383
|
+
'"bandwidth-limit", jws, word, jsemi',
|
|
384
|
+
lambda x: ("bandwidth-limit", x[0]),
|
|
385
|
+
),
|
|
386
|
+
S("junos_bw_perc"): (
|
|
387
|
+
'"bandwidth-percent", jws, alphanums, jsemi',
|
|
388
|
+
lambda x: ("bandwidth-percent", x[0]),
|
|
389
|
+
),
|
|
390
|
+
S("junos_burst_limit"): (
|
|
391
|
+
'"burst-size-limit", jws, alphanums, jsemi',
|
|
392
|
+
lambda x: ("burst-size-limit", x[0]),
|
|
393
|
+
),
|
|
394
|
+
S("junos_match"): (" / ".join(junos_match_types), dict_sum),
|
|
395
|
+
S("junos_action"): (
|
|
396
|
+
"junos_one_action / junos_reject_action /"
|
|
397
|
+
"junos_reject_action / junos_ri_action",
|
|
398
|
+
lambda x: {"action": x[0]},
|
|
399
|
+
),
|
|
400
|
+
"junos_one_action": ('"accept" / "discard" / "reject" / ("next", jws, "term")'),
|
|
401
|
+
"junos_reject_action": (
|
|
402
|
+
'"reject", jws, ' + literals(icmp_reject_codes),
|
|
403
|
+
lambda x: ("reject", x),
|
|
404
|
+
),
|
|
405
|
+
S("junos_ri_action"): (
|
|
406
|
+
'"routing-instance", jws, jword',
|
|
407
|
+
lambda x: ("routing-instance", x[0]),
|
|
408
|
+
),
|
|
409
|
+
S("junos_modifier"): (
|
|
410
|
+
"junos_one_modifier / junos_arg_modifier",
|
|
411
|
+
lambda x: {"modifiers": x},
|
|
412
|
+
),
|
|
413
|
+
"junos_one_modifier": (
|
|
414
|
+
'"log" / "sample" / "syslog" / "port-mirror"',
|
|
415
|
+
lambda x: (x, True),
|
|
416
|
+
),
|
|
417
|
+
S("junos_arg_modifier"): "junos_arg_modifier_kw, jws, jword",
|
|
418
|
+
"junos_arg_modifier_kw": (
|
|
419
|
+
'"count" / "forwarding-class" / "ipsec-sa" /"loss-priority" / "policer"'
|
|
420
|
+
),
|
|
421
|
+
}
|
|
422
|
+
)
|
trigger/acl/models.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Database models for the task queue.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import datetime
|
|
6
|
+
|
|
7
|
+
import peewee as pw
|
|
8
|
+
|
|
9
|
+
from trigger.conf import settings
|
|
10
|
+
|
|
11
|
+
engine = settings.DATABASE_ENGINE
|
|
12
|
+
if not engine:
|
|
13
|
+
raise RuntimeError("You must specify a database engine in settings.DATABASE_ENGINE")
|
|
14
|
+
|
|
15
|
+
# We're hard-coding support for the BIG THREE database solutions for now,
|
|
16
|
+
# because that's what the ``peewee`` library we are using as the ORM supports.
|
|
17
|
+
if engine == "sqlite3":
|
|
18
|
+
database = pw.SqliteDatabase(database=settings.DATABASE_NAME)
|
|
19
|
+
elif engine == "mysql":
|
|
20
|
+
if not settings.DATABASE_PORT:
|
|
21
|
+
settings.DATABASE_PORT = 3306
|
|
22
|
+
database = pw.MySQLDatabase(
|
|
23
|
+
host=settings.DATABASE_HOST,
|
|
24
|
+
database=settings.DATABASE_NAME,
|
|
25
|
+
port=settings.DATABASE_PORT,
|
|
26
|
+
user=settings.DATABASE_USER,
|
|
27
|
+
passwd=settings.DATABASE_PASSWORD,
|
|
28
|
+
)
|
|
29
|
+
elif engine == "postgresql":
|
|
30
|
+
database = pw.PostgresqlDatabase(
|
|
31
|
+
host=settings.DATABASE_HOST,
|
|
32
|
+
database=settings.DATABASE_NAME,
|
|
33
|
+
port=settings.DATABASE_PORT,
|
|
34
|
+
user=settings.DATABASE_USER,
|
|
35
|
+
password=settings.DATABASE_PASSWORD,
|
|
36
|
+
)
|
|
37
|
+
else:
|
|
38
|
+
raise RuntimeError(f"Unsupported database engine: {engine}")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class BaseModel(pw.Model):
|
|
42
|
+
"""
|
|
43
|
+
Base model that inherits the database object determined above.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
class Meta:
|
|
47
|
+
database = database
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CustomCharField(pw.CharField):
|
|
51
|
+
"""Overload default CharField to always return strings vs. UTF-8"""
|
|
52
|
+
|
|
53
|
+
def coerce(self, value):
|
|
54
|
+
return str(value or "")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class IntegratedTask(BaseModel):
|
|
58
|
+
"""
|
|
59
|
+
Tasks for "integrated" queue used by `~trigger.acl.queue.Queue`.
|
|
60
|
+
|
|
61
|
+
e.g. ``acl -l``
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
# Python 3 / peewee v3+: PrimaryKeyField renamed to AutoField
|
|
65
|
+
id = pw.AutoField()
|
|
66
|
+
acl = CustomCharField(null=False, default="")
|
|
67
|
+
router = CustomCharField(null=False, default="")
|
|
68
|
+
queued = pw.DateTimeField(default=datetime.datetime.now)
|
|
69
|
+
loaded = pw.DateTimeField(null=True)
|
|
70
|
+
escalation = pw.BooleanField(default=False)
|
|
71
|
+
|
|
72
|
+
class Meta:
|
|
73
|
+
# Python 3 / peewee v3+: db_table renamed to table_name
|
|
74
|
+
table_name = "acl_queue"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class ManualTask(BaseModel):
|
|
78
|
+
"""
|
|
79
|
+
Tasks for "manual" queue used by `~trigger.acl.queue.Queue`.
|
|
80
|
+
|
|
81
|
+
e.g. ``acl -m``
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
q_id = pw.AutoField()
|
|
85
|
+
q_ts = pw.DateTimeField(default=datetime.datetime.now)
|
|
86
|
+
q_name = CustomCharField(null=False)
|
|
87
|
+
q_routers = CustomCharField(null=False, default="")
|
|
88
|
+
done = pw.BooleanField(default=False)
|
|
89
|
+
q_sr = pw.IntegerField(null=False, default=0)
|
|
90
|
+
login = CustomCharField(null=False, default="")
|
|
91
|
+
|
|
92
|
+
class Meta:
|
|
93
|
+
table_name = "queue"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
MODEL_MAP = {
|
|
97
|
+
"integrated": IntegratedTask,
|
|
98
|
+
"manual": ManualTask,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def create_tables():
|
|
103
|
+
"""Connect to the database and create the tables for each model."""
|
|
104
|
+
database.connect()
|
|
105
|
+
IntegratedTask.create_table()
|
|
106
|
+
ManualTask.create_table()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def confirm_tables():
|
|
110
|
+
"""Ensure the table exists for each model."""
|
|
111
|
+
print("Checking tables...")
|
|
112
|
+
width = max(len(q_name) for q_name in MODEL_MAP)
|
|
113
|
+
for q_name, model in MODEL_MAP.items():
|
|
114
|
+
print(q_name.ljust(width), end=" ")
|
|
115
|
+
print(model.table_exists())
|
|
116
|
+
else:
|
|
117
|
+
return True
|
|
118
|
+
return False
|