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
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
acl_script - CLI interface to simplify complex modification to access-lists.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# TODO (jathan): Have this import from trigger.acl.utils.AclScript, because
|
|
9
|
+
# much of the code is copypasta.
|
|
10
|
+
|
|
11
|
+
__version__ = "1.2"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import re
|
|
16
|
+
import sys
|
|
17
|
+
from optparse import OptionParser
|
|
18
|
+
|
|
19
|
+
import trigger.acl.tools as acl_tools
|
|
20
|
+
from trigger.acl.parser import TIP, Comment, parse
|
|
21
|
+
from trigger.utils.cli import yesno
|
|
22
|
+
from trigger.utils.rcs import RCS
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def parse_args(argv):
|
|
26
|
+
notes = ""
|
|
27
|
+
parser = OptionParser(
|
|
28
|
+
usage="%prog [options]",
|
|
29
|
+
add_help_option=0,
|
|
30
|
+
description="""ACL modify/generator from the commandline.""",
|
|
31
|
+
)
|
|
32
|
+
parser.add_option("-h", "--help", action="store_false")
|
|
33
|
+
parser.add_option(
|
|
34
|
+
"-a", "--acl", action="append", type="string", help="specify the acl file"
|
|
35
|
+
)
|
|
36
|
+
parser.add_option(
|
|
37
|
+
"-n", "--no-changes", action="store_true", help="don't make the changes"
|
|
38
|
+
)
|
|
39
|
+
parser.add_option(
|
|
40
|
+
"--show-mods",
|
|
41
|
+
action="store_true",
|
|
42
|
+
help="show modifications being made in a simple format.",
|
|
43
|
+
)
|
|
44
|
+
parser.add_option(
|
|
45
|
+
"--no-worklog", action="store_true", help="don't make a worklog entry"
|
|
46
|
+
)
|
|
47
|
+
parser.add_option(
|
|
48
|
+
"-N",
|
|
49
|
+
"--no-input",
|
|
50
|
+
action="store_true",
|
|
51
|
+
help="require no input (good for scripts)",
|
|
52
|
+
)
|
|
53
|
+
parser.add_option(
|
|
54
|
+
"-s",
|
|
55
|
+
"--source-address",
|
|
56
|
+
action="append",
|
|
57
|
+
type="string",
|
|
58
|
+
default=[],
|
|
59
|
+
help="define a source address",
|
|
60
|
+
)
|
|
61
|
+
parser.add_option(
|
|
62
|
+
"-d",
|
|
63
|
+
"--destination-address",
|
|
64
|
+
action="append",
|
|
65
|
+
type="string",
|
|
66
|
+
default=[],
|
|
67
|
+
help="define a destination address",
|
|
68
|
+
)
|
|
69
|
+
parser.add_option(
|
|
70
|
+
"--destination-address-from-file",
|
|
71
|
+
action="append",
|
|
72
|
+
type="string",
|
|
73
|
+
default=[],
|
|
74
|
+
help="read a set of destination-addresses from a file",
|
|
75
|
+
)
|
|
76
|
+
parser.add_option(
|
|
77
|
+
"--source-address-from-file",
|
|
78
|
+
default=[],
|
|
79
|
+
action="append",
|
|
80
|
+
type="string",
|
|
81
|
+
help="read a set of source-addresses from a file",
|
|
82
|
+
)
|
|
83
|
+
parser.add_option(
|
|
84
|
+
"--protocol",
|
|
85
|
+
default=[],
|
|
86
|
+
action="append",
|
|
87
|
+
type="string",
|
|
88
|
+
help="define a protocol",
|
|
89
|
+
)
|
|
90
|
+
parser.add_option(
|
|
91
|
+
"-p",
|
|
92
|
+
"--source-port",
|
|
93
|
+
default=[],
|
|
94
|
+
action="append",
|
|
95
|
+
type="int",
|
|
96
|
+
help="define a source-port",
|
|
97
|
+
)
|
|
98
|
+
parser.add_option(
|
|
99
|
+
"",
|
|
100
|
+
"--source-port-range",
|
|
101
|
+
default=[],
|
|
102
|
+
action="append",
|
|
103
|
+
type="string",
|
|
104
|
+
nargs=2,
|
|
105
|
+
help="define a source-port range",
|
|
106
|
+
)
|
|
107
|
+
parser.add_option(
|
|
108
|
+
"",
|
|
109
|
+
"--destination-port-range",
|
|
110
|
+
default=[],
|
|
111
|
+
action="append",
|
|
112
|
+
type="string",
|
|
113
|
+
nargs=2,
|
|
114
|
+
help="define a destination-port range",
|
|
115
|
+
)
|
|
116
|
+
parser.add_option(
|
|
117
|
+
"-P",
|
|
118
|
+
"--destination-port",
|
|
119
|
+
default=[],
|
|
120
|
+
action="append",
|
|
121
|
+
type="int",
|
|
122
|
+
help="define a destination port",
|
|
123
|
+
)
|
|
124
|
+
parser.add_option(
|
|
125
|
+
"-t",
|
|
126
|
+
"--modify-specific-term",
|
|
127
|
+
default=[],
|
|
128
|
+
action="append",
|
|
129
|
+
type="string",
|
|
130
|
+
help="""When modifying a JUNOS type ACL, you may specify this option one or more times
|
|
131
|
+
to define a specific JUNOS term you want to modify. This takes one argument
|
|
132
|
+
which should be the name of term.
|
|
133
|
+
""",
|
|
134
|
+
)
|
|
135
|
+
parser.add_option(
|
|
136
|
+
"-c",
|
|
137
|
+
"--modify-between-comments",
|
|
138
|
+
action="append",
|
|
139
|
+
type="string",
|
|
140
|
+
nargs=2,
|
|
141
|
+
help="""When modifying a IOS type ACL, you may specify this option one or more times
|
|
142
|
+
to define a specific AREA of the ACL you want to modify. You must have at least
|
|
143
|
+
2 comments defined in the ACL prior to running. This requires two arguments, the
|
|
144
|
+
start comment, and the end comment. Your modifications will be done between the
|
|
145
|
+
two.
|
|
146
|
+
""",
|
|
147
|
+
)
|
|
148
|
+
parser.add_option(
|
|
149
|
+
"-C",
|
|
150
|
+
"--comment",
|
|
151
|
+
type="string",
|
|
152
|
+
help="Add a comment when making the modification.",
|
|
153
|
+
)
|
|
154
|
+
parser.add_option(
|
|
155
|
+
"--insert-defined",
|
|
156
|
+
default=False,
|
|
157
|
+
action="store_true",
|
|
158
|
+
help="""This option works differently based on the type of ACL we are modifying. The
|
|
159
|
+
one similar characteristic is that this will never remove any access already defined, just
|
|
160
|
+
append.
|
|
161
|
+
""",
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
notes += """
|
|
165
|
+
NOTE when using --insert-defined:
|
|
166
|
+
On a JUNOS type acl using --insert-defined, this will only replace parts of the
|
|
167
|
+
term that have been specified on the command-line. This may sound confusing
|
|
168
|
+
but this example should clear things up.
|
|
169
|
+
|
|
170
|
+
Take the following term:
|
|
171
|
+
term sr31337 {
|
|
172
|
+
from {
|
|
173
|
+
source-address {
|
|
174
|
+
10.0.0.0/8;
|
|
175
|
+
11.0.0.0/8;
|
|
176
|
+
}
|
|
177
|
+
destination-address {
|
|
178
|
+
192.168.0.1/32;
|
|
179
|
+
}
|
|
180
|
+
destination-port 80;
|
|
181
|
+
protocol tcp;
|
|
182
|
+
}
|
|
183
|
+
then {
|
|
184
|
+
accept;
|
|
185
|
+
count sr31337;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
If you run this with the following arguments:
|
|
190
|
+
--modify-specific-term sr31337 --source-address 5.5.5.5/32 --destination-port 81 --insert-defined
|
|
191
|
+
The following term will be generated:
|
|
192
|
+
term sr31337 {
|
|
193
|
+
from {
|
|
194
|
+
source-address {
|
|
195
|
+
5.5.5.5/32;
|
|
196
|
+
10.0.0.0/8;
|
|
197
|
+
11.0.0.0/8;
|
|
198
|
+
}
|
|
199
|
+
destination-address {
|
|
200
|
+
192.168.0.1/32;
|
|
201
|
+
}
|
|
202
|
+
destination-port 80-81;
|
|
203
|
+
protocol tcp;
|
|
204
|
+
}
|
|
205
|
+
then {
|
|
206
|
+
accept;
|
|
207
|
+
count sr31337;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
As you can see 5.5.5.5/32 was added to the source-address portion, and 81
|
|
212
|
+
was added as a destination-port. Notice that all other fields were left
|
|
213
|
+
alone.
|
|
214
|
+
|
|
215
|
+
On a IOS type acl using --insert-defined, this will only add access where
|
|
216
|
+
it is needed. Take the following example:
|
|
217
|
+
|
|
218
|
+
!!! I AM L33T
|
|
219
|
+
access-list 101 permit udp host 192.168.0.1 host 192.168.1.1 eq 80
|
|
220
|
+
access-list 101 permit ip host 192.168.0.5 host 192.168.1.10
|
|
221
|
+
access-list 101 permit tcp host 192.168.0.6 host 192.168.1.11 eq 22
|
|
222
|
+
!!! I AM NOT L33T
|
|
223
|
+
|
|
224
|
+
If you run this with the following arguments:
|
|
225
|
+
--modify-between-comments "I AM L33T" "I AM NOT L33T" \\
|
|
226
|
+
--source-address 192.168.0.5 \\
|
|
227
|
+
--destination-address 192.168.1.10 \\
|
|
228
|
+
--destination-address 192.168.1.11 \\
|
|
229
|
+
--protocol tcp \\
|
|
230
|
+
--destination-port 80 \\
|
|
231
|
+
--insert-defined
|
|
232
|
+
The following will be generated:
|
|
233
|
+
!!! I AM L33T
|
|
234
|
+
access-list 101 permit udp host 192.168.0.1 host 192.168.1.1 eq 80
|
|
235
|
+
access-list 101 permit ip host 192.168.0.5 host 192.168.1.10
|
|
236
|
+
access-list 101 permit tcp host 192.168.0.6 host 192.168.1.11 eq 22
|
|
237
|
+
access-list 101 permit tcp host 192.168.0.5 host 192.168.1.11 eq 80
|
|
238
|
+
!!! I AM NOT L33T
|
|
239
|
+
|
|
240
|
+
As you can see the last line was added, take note that the
|
|
241
|
+
192.168.0.5->192.168.1.10:80 access was not added because it
|
|
242
|
+
was already allowed.
|
|
243
|
+
"""
|
|
244
|
+
|
|
245
|
+
parser.add_option(
|
|
246
|
+
"--replace-defined",
|
|
247
|
+
default=False,
|
|
248
|
+
action="store_true",
|
|
249
|
+
help="""This option works differently based on the type of ACL we are modifying. The
|
|
250
|
+
one similar characteristic is that access can be removed, since this replaces
|
|
251
|
+
whole sets of defined data.
|
|
252
|
+
""",
|
|
253
|
+
)
|
|
254
|
+
notes += """
|
|
255
|
+
|
|
256
|
+
NOTE when using --replace-defined:
|
|
257
|
+
When modifying a JUNOS term you only replace parts of the term that are
|
|
258
|
+
specified on the commandline.
|
|
259
|
+
|
|
260
|
+
Take the following as an example:
|
|
261
|
+
term sr31337 {
|
|
262
|
+
from {
|
|
263
|
+
source-address {
|
|
264
|
+
10.0.0.0/8;
|
|
265
|
+
11.0.0.0/8;
|
|
266
|
+
}
|
|
267
|
+
destination-address {
|
|
268
|
+
192.168.0.1/32;
|
|
269
|
+
}
|
|
270
|
+
destination-port 80;
|
|
271
|
+
protocol tcp;
|
|
272
|
+
}
|
|
273
|
+
then {
|
|
274
|
+
accept;
|
|
275
|
+
count sr31337;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
If you run this with the following arguments:
|
|
279
|
+
--modify-specific-term sr31337 --source-address 5.5.5.5 --replace-defined
|
|
280
|
+
The following is generated:
|
|
281
|
+
term sr31337 {
|
|
282
|
+
from {
|
|
283
|
+
source-address {
|
|
284
|
+
5.5.5.5/32;
|
|
285
|
+
}
|
|
286
|
+
destination-address {
|
|
287
|
+
192.168.0.1/32;
|
|
288
|
+
}
|
|
289
|
+
destination-port 80;
|
|
290
|
+
protocol tcp;
|
|
291
|
+
}
|
|
292
|
+
then {
|
|
293
|
+
accept;
|
|
294
|
+
count sr31337;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
As you can see only the source-address portion of the term was replaced.
|
|
299
|
+
"""
|
|
300
|
+
opts, args = parser.parse_args(argv)
|
|
301
|
+
# print 'OPTS:', opts
|
|
302
|
+
# print 'ARGS:', args
|
|
303
|
+
|
|
304
|
+
for i in opts.source_port_range:
|
|
305
|
+
opts.source_port.append(i)
|
|
306
|
+
for i in opts.destination_port_range:
|
|
307
|
+
opts.destination_port.append(i)
|
|
308
|
+
|
|
309
|
+
cnt = 0
|
|
310
|
+
for i in opts.source_address:
|
|
311
|
+
opts.source_address[cnt] = TIP(i)
|
|
312
|
+
cnt += 1
|
|
313
|
+
|
|
314
|
+
cnt = 0
|
|
315
|
+
for i in opts.destination_address:
|
|
316
|
+
opts.destination_address[cnt] = TIP(i)
|
|
317
|
+
cnt += 1
|
|
318
|
+
for i in opts.destination_address_from_file:
|
|
319
|
+
f = open(i)
|
|
320
|
+
line = f.readline()
|
|
321
|
+
while line:
|
|
322
|
+
line = line.rstrip()
|
|
323
|
+
opts.destination_address.append(TIP(line))
|
|
324
|
+
line = f.readline()
|
|
325
|
+
for i in opts.source_address_from_file:
|
|
326
|
+
f = open(i)
|
|
327
|
+
line = f.readline()
|
|
328
|
+
while line:
|
|
329
|
+
line = line.rstrip()
|
|
330
|
+
opts.source_address.append(TIP(line))
|
|
331
|
+
line = f.readline()
|
|
332
|
+
|
|
333
|
+
if len(argv) == 1 or not opts.acl or opts.help:
|
|
334
|
+
parser.print_help()
|
|
335
|
+
print(notes)
|
|
336
|
+
sys.exit(2)
|
|
337
|
+
|
|
338
|
+
if not opts.replace_defined or opts.insert_defined:
|
|
339
|
+
opts.insert_defined = True
|
|
340
|
+
|
|
341
|
+
return opts, args
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def log_term(term, msg="ADDING"):
|
|
345
|
+
print(f">>{msg}<<", end=" ")
|
|
346
|
+
for k, v in term.match.items():
|
|
347
|
+
for x in v:
|
|
348
|
+
print(f"KEY:{k} VAL:{x},", end=" ")
|
|
349
|
+
print("")
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def wedge_acl(acl, new_term, between, opts):
|
|
353
|
+
if not between:
|
|
354
|
+
# ugg, don't deal with this yet.
|
|
355
|
+
return
|
|
356
|
+
elif isinstance(between, tuple):
|
|
357
|
+
found_start = False
|
|
358
|
+
found_end = False
|
|
359
|
+
start_offset = 0
|
|
360
|
+
end_offset = 0
|
|
361
|
+
offset = 0
|
|
362
|
+
for term in acl.terms:
|
|
363
|
+
for comment in term.comments:
|
|
364
|
+
if found_start and between[1] in comment:
|
|
365
|
+
end_offset = offset
|
|
366
|
+
found_end = True
|
|
367
|
+
break
|
|
368
|
+
elif not found_start and between[0] in comment:
|
|
369
|
+
start_offset = offset
|
|
370
|
+
found_start = True
|
|
371
|
+
if found_start and found_end:
|
|
372
|
+
break
|
|
373
|
+
offset += 1
|
|
374
|
+
if found_start and found_end:
|
|
375
|
+
# everthing before the start of the modify
|
|
376
|
+
head_terms = acl.terms[0:start_offset]
|
|
377
|
+
# everything after the modify portion.
|
|
378
|
+
footer_terms = acl.terms[end_offset:]
|
|
379
|
+
# the terms to modify
|
|
380
|
+
terms = acl.terms[start_offset:end_offset]
|
|
381
|
+
# create a new term and add the head to it
|
|
382
|
+
create_term = head_terms
|
|
383
|
+
if opts.replace_defined:
|
|
384
|
+
# don't forget about the comment
|
|
385
|
+
# or else future edits will be gone.
|
|
386
|
+
new_term.comments = acl.terms[start_offset].comments
|
|
387
|
+
create_term.append(new_term)
|
|
388
|
+
if opts.show_mods:
|
|
389
|
+
# log the added
|
|
390
|
+
log_term(new_term)
|
|
391
|
+
# log the removed
|
|
392
|
+
for term in terms:
|
|
393
|
+
log_term(term, "REMOVING")
|
|
394
|
+
elif opts.insert_defined:
|
|
395
|
+
to_insert = acl_tools.create_access(terms, new_term)
|
|
396
|
+
# put all the other stuff in here.
|
|
397
|
+
for term in terms:
|
|
398
|
+
create_term.append(term)
|
|
399
|
+
# just insert the new entries at the bottom...
|
|
400
|
+
if to_insert:
|
|
401
|
+
to_insert[0].comments.append(
|
|
402
|
+
Comment("Added by acl_script on <date>")
|
|
403
|
+
)
|
|
404
|
+
for toins in to_insert:
|
|
405
|
+
if opts.show_mods:
|
|
406
|
+
log_term(toins)
|
|
407
|
+
create_term.append(toins)
|
|
408
|
+
# add the end
|
|
409
|
+
for term in footer_terms:
|
|
410
|
+
create_term.append(term)
|
|
411
|
+
# overwrite the original list.
|
|
412
|
+
acl.terms = create_term
|
|
413
|
+
elif isinstance(between, str):
|
|
414
|
+
# find a specific term to modify
|
|
415
|
+
assert acl.format == "junos"
|
|
416
|
+
for term in acl.terms:
|
|
417
|
+
if term.name == between:
|
|
418
|
+
if opts.replace_defined:
|
|
419
|
+
# for every part of new_term that is defined
|
|
420
|
+
# we replace that portion in this term.
|
|
421
|
+
for k, v in new_term.match.items():
|
|
422
|
+
if opts.show_mods:
|
|
423
|
+
removing = term.match.get(k, [])
|
|
424
|
+
for x in removing:
|
|
425
|
+
print(f">>REMOVING<< KEY:{k} VAL:{x},TERM:{term.name}")
|
|
426
|
+
for x in v:
|
|
427
|
+
print(f">>ADDING<< KEY:{k} VAL:{x},TERM:{term.name}")
|
|
428
|
+
term.match[k] = v
|
|
429
|
+
|
|
430
|
+
elif opts.insert_defined:
|
|
431
|
+
# for every part of new_term that is defined
|
|
432
|
+
# we just insert if not found in the section
|
|
433
|
+
# of this term.
|
|
434
|
+
for k, v in new_term.match.items():
|
|
435
|
+
if not term.match.has_key(k):
|
|
436
|
+
if opts.show_mods:
|
|
437
|
+
for x in v:
|
|
438
|
+
print(
|
|
439
|
+
f">>ADDING<< KEY:{k} VAL:{x}, TERM:{term.name}"
|
|
440
|
+
)
|
|
441
|
+
term.match[k] = v
|
|
442
|
+
else:
|
|
443
|
+
if opts.comment:
|
|
444
|
+
for comment in term.comments:
|
|
445
|
+
if opts.comment == comment.data:
|
|
446
|
+
break
|
|
447
|
+
else:
|
|
448
|
+
term.comments.append(Comment(opts.comment))
|
|
449
|
+
for x in v:
|
|
450
|
+
print(x)
|
|
451
|
+
if x not in term.match[k]:
|
|
452
|
+
if opts.show_mods:
|
|
453
|
+
print(
|
|
454
|
+
f">>ADDING<< KEY:{k} VAL:{x}, TERM:{term.name}"
|
|
455
|
+
)
|
|
456
|
+
term.match[k].append(x)
|
|
457
|
+
break
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def main():
|
|
461
|
+
"""Main entry point for the CLI tool."""
|
|
462
|
+
opts, args = parse_args(sys.argv)
|
|
463
|
+
for acl_file in opts.acl:
|
|
464
|
+
rcs = RCS(acl_file, create=False)
|
|
465
|
+
rcs.lock_loop()
|
|
466
|
+
|
|
467
|
+
try:
|
|
468
|
+
with open(acl_file) as f:
|
|
469
|
+
acl = parse(f)
|
|
470
|
+
# TODO (jathan): Improve this naked except
|
|
471
|
+
except Exception:
|
|
472
|
+
print(f">>ERROR<< Could not parse acl file {acl_file}!")
|
|
473
|
+
rcs.unlock()
|
|
474
|
+
sys.exit(2)
|
|
475
|
+
|
|
476
|
+
short_acl = acl_file
|
|
477
|
+
r = re.compile(r"\/(acl\..*?)$")
|
|
478
|
+
ar = r.findall(acl_file) # opts.acl)
|
|
479
|
+
if ar:
|
|
480
|
+
short_acl = ar[0]
|
|
481
|
+
|
|
482
|
+
term = acl_tools.create_trigger_term(
|
|
483
|
+
opts.source_address,
|
|
484
|
+
opts.destination_address,
|
|
485
|
+
opts.source_port,
|
|
486
|
+
opts.destination_port,
|
|
487
|
+
opts.protocol,
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
# Comments are not integrated and handled differently
|
|
491
|
+
if opts.comment:
|
|
492
|
+
term.comments.append(opts.comment)
|
|
493
|
+
|
|
494
|
+
if opts.modify_between_comments and not acl.format.startswith("ios"):
|
|
495
|
+
print("--modify-between-comments should only be used for ios like acls")
|
|
496
|
+
rcs.unlock()
|
|
497
|
+
sys.exit(1)
|
|
498
|
+
|
|
499
|
+
if opts.modify_specific_term and acl.format != "junos":
|
|
500
|
+
print("--modify-specific-term should only be used on junos like acls")
|
|
501
|
+
rcs.unlock()
|
|
502
|
+
sys.exit(1)
|
|
503
|
+
|
|
504
|
+
if opts.modify_between_comments:
|
|
505
|
+
for d in opts.modify_between_comments:
|
|
506
|
+
wedge_acl(acl, term, d, opts)
|
|
507
|
+
elif opts.modify_specific_term:
|
|
508
|
+
for d in opts.modify_specific_term:
|
|
509
|
+
wedge_acl(acl, term, d, opts)
|
|
510
|
+
|
|
511
|
+
tempfile = acl_tools.write_tmpacl(acl, process_name="_acl_script")
|
|
512
|
+
diff = acl_tools.diff_files(acl_file, tempfile)
|
|
513
|
+
|
|
514
|
+
if not diff:
|
|
515
|
+
print(f"No changes made to {acl_file}")
|
|
516
|
+
rcs.unlock()
|
|
517
|
+
os.remove(tempfile)
|
|
518
|
+
# sys.exit(0)
|
|
519
|
+
continue
|
|
520
|
+
|
|
521
|
+
prestr = ""
|
|
522
|
+
if opts.no_input:
|
|
523
|
+
prestr = ">>DIFF<< "
|
|
524
|
+
|
|
525
|
+
print(f'{prestr}"{acl_file}"') # opts.acl)
|
|
526
|
+
print(f"{prestr}BEGINNING OF CHANGES ===========")
|
|
527
|
+
for l in diff.split("\n"):
|
|
528
|
+
print(f"{prestr}{l}")
|
|
529
|
+
print(f"{prestr}END OF CHANGES =================")
|
|
530
|
+
|
|
531
|
+
if not opts.no_input:
|
|
532
|
+
if not yesno("Do you want to save changes?"):
|
|
533
|
+
rcs.unlock()
|
|
534
|
+
os.remove(tempfile)
|
|
535
|
+
# sys.exit(1)
|
|
536
|
+
continue
|
|
537
|
+
|
|
538
|
+
if not opts.no_changes:
|
|
539
|
+
import shutil
|
|
540
|
+
|
|
541
|
+
shutil.copy(tempfile, acl_file) # opts.acl)
|
|
542
|
+
|
|
543
|
+
# rcs.checkin() #message=opts.comment)
|
|
544
|
+
pats = (re.compile(r"^\+.*!+(.*)"), re.compile(r"^\+.*/\*(.*)\*/"))
|
|
545
|
+
logstr = "From acl_script\n"
|
|
546
|
+
for line in diff.split("\n"):
|
|
547
|
+
for pat in pats:
|
|
548
|
+
m = pat.match(line)
|
|
549
|
+
if m:
|
|
550
|
+
msg = m.group(1).strip()
|
|
551
|
+
if msg:
|
|
552
|
+
logstr += m.group(1).strip() + "\n"
|
|
553
|
+
break
|
|
554
|
+
if logstr:
|
|
555
|
+
print("Autodetected log message:")
|
|
556
|
+
print(logstr)
|
|
557
|
+
print("")
|
|
558
|
+
else:
|
|
559
|
+
logstr = ""
|
|
560
|
+
# TODO (jathan): Replace this with rcs.checkin()
|
|
561
|
+
os.spawnlp(
|
|
562
|
+
os.P_WAIT, "ci", "ci", "-u", "-m" + logstr, acl_file
|
|
563
|
+
) # old_file)
|
|
564
|
+
# os.remove(tmpfile)
|
|
565
|
+
|
|
566
|
+
if not opts.no_worklog:
|
|
567
|
+
acl_tools.worklog(short_acl, diff, log_string="updated by acl_script")
|
|
568
|
+
|
|
569
|
+
rcs.unlock()
|
|
570
|
+
os.remove(tempfile)
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
if __name__ == "__main__":
|
|
574
|
+
main()
|
trigger/bin/aclconv.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
aclconv - Converts an ACL, or a list of ACLs, from one format to another.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
__version__ = "1.11"
|
|
8
|
+
|
|
9
|
+
import optparse
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
from trigger import acl
|
|
13
|
+
|
|
14
|
+
formats = {
|
|
15
|
+
"ios": ("i", "IOS old-school"),
|
|
16
|
+
"ios_named": ("o", "IOS named"),
|
|
17
|
+
"junos": ("j", "JunOS"),
|
|
18
|
+
"iosxr": ("x", "IOS XR"),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main():
|
|
23
|
+
"""Main entry point for the CLI tool."""
|
|
24
|
+
optp = optparse.OptionParser(
|
|
25
|
+
description="""\
|
|
26
|
+
Convert an ACL on stdin, or a list of ACLs, from one format to another.
|
|
27
|
+
|
|
28
|
+
Input format is determined automatically. Output format can be given
|
|
29
|
+
with -f or with one of -i/-o/-j/-x.
|
|
30
|
+
|
|
31
|
+
The name of the output ACL is determined automatically, or it can be
|
|
32
|
+
specified with -n."""
|
|
33
|
+
)
|
|
34
|
+
optp.add_option("-f", "--format", choices=list(formats.keys()))
|
|
35
|
+
for format, t in formats.items():
|
|
36
|
+
optp.add_option(
|
|
37
|
+
"-" + t[0],
|
|
38
|
+
"--" + format.replace("_", "-"),
|
|
39
|
+
action="store_const",
|
|
40
|
+
const=format,
|
|
41
|
+
dest="format",
|
|
42
|
+
help=f"Use {t[1]} ACL output format",
|
|
43
|
+
)
|
|
44
|
+
optp.add_option("-n", "--name", dest="aclname")
|
|
45
|
+
(opts, files) = optp.parse_args()
|
|
46
|
+
|
|
47
|
+
if opts.format is None:
|
|
48
|
+
optp.print_help()
|
|
49
|
+
sys.exit(1)
|
|
50
|
+
|
|
51
|
+
if not files or files == ["-"]:
|
|
52
|
+
files = ["/dev/fd/0"]
|
|
53
|
+
|
|
54
|
+
for fname in files:
|
|
55
|
+
with open(fname) as f:
|
|
56
|
+
a = acl.parse(f)
|
|
57
|
+
|
|
58
|
+
if opts.aclname is not None:
|
|
59
|
+
a.name = opts.aclname
|
|
60
|
+
elif opts.format == "ios" and (a.name is None or not a.name.isdigit()):
|
|
61
|
+
a.name = "100"
|
|
62
|
+
elif opts.format != "ios" and not a.name.endswith(formats[opts.format][0]):
|
|
63
|
+
a.name += formats[opts.format][0]
|
|
64
|
+
|
|
65
|
+
if opts.format.startswith("ios"):
|
|
66
|
+
for t in a.terms:
|
|
67
|
+
if t.action == ("discard",):
|
|
68
|
+
t.action = "reject"
|
|
69
|
+
if opts.format == "junos":
|
|
70
|
+
a.name_terms()
|
|
71
|
+
for t in a.terms:
|
|
72
|
+
if "count" not in t.modifiers:
|
|
73
|
+
t.modifiers["count"] = t.name
|
|
74
|
+
if opts.format == "iosxr":
|
|
75
|
+
for t in a.terms:
|
|
76
|
+
t.name = None
|
|
77
|
+
|
|
78
|
+
print("\n".join(a.output(opts.format, replace=True)))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if __name__ == "__main__":
|
|
82
|
+
main()
|