trigger 2.0.0__tar.gz → 2.0.3__tar.gz
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-2.0.0/trigger.egg-info → trigger-2.0.3}/PKG-INFO +1 -1
- trigger-2.0.3/pyproject.toml +236 -0
- {trigger-2.0.0 → trigger-2.0.3}/tests/test_acl.py +15 -22
- {trigger-2.0.0 → trigger-2.0.3}/tests/test_acl_queue.py +6 -2
- {trigger-2.0.0 → trigger-2.0.3}/tests/test_except.py +2 -2
- {trigger-2.0.0 → trigger-2.0.3}/tests/test_netdevices.py +6 -8
- {trigger-2.0.0 → trigger-2.0.3}/tests/test_scripts.py +3 -2
- {trigger-2.0.0 → trigger-2.0.3}/tests/test_tacacsrc.py +19 -14
- {trigger-2.0.0 → trigger-2.0.3}/tests/test_templates.py +6 -2
- {trigger-2.0.0 → trigger-2.0.3}/tests/test_twister2.py +0 -4
- {trigger-2.0.0 → trigger-2.0.3}/trigger/acl/__init__.py +2 -3
- {trigger-2.0.0 → trigger-2.0.3}/trigger/acl/autoacl.py +3 -5
- {trigger-2.0.0 → trigger-2.0.3}/trigger/acl/db.py +35 -47
- {trigger-2.0.0 → trigger-2.0.3}/trigger/acl/dicts.py +2 -3
- {trigger-2.0.0 → trigger-2.0.3}/trigger/acl/grammar.py +3 -5
- {trigger-2.0.0 → trigger-2.0.3}/trigger/acl/ios.py +11 -12
- {trigger-2.0.0 → trigger-2.0.3}/trigger/acl/junos.py +26 -38
- {trigger-2.0.0 → trigger-2.0.3}/trigger/acl/models.py +16 -16
- {trigger-2.0.0 → trigger-2.0.3}/trigger/acl/parser.py +28 -34
- {trigger-2.0.0 → trigger-2.0.3}/trigger/acl/queue.py +48 -47
- {trigger-2.0.0 → trigger-2.0.3}/trigger/acl/support.py +147 -161
- {trigger-2.0.0 → trigger-2.0.3}/trigger/acl/tools.py +107 -122
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/acl.py +8 -6
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/acl_script.py +48 -46
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/aclconv.py +4 -5
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/check_access.py +10 -13
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/check_syntax.py +10 -10
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/fe.py +25 -23
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/find_access.py +15 -20
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/gnng.py +24 -35
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/gong.py +5 -3
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/load_acl.py +72 -67
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/load_config.py +1 -2
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/netdev.py +18 -23
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/optimizer.py +29 -41
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/run_cmds.py +1 -2
- {trigger-2.0.0 → trigger-2.0.3}/trigger/changemgmt/__init__.py +17 -29
- {trigger-2.0.0 → trigger-2.0.3}/trigger/changemgmt/bounce.py +3 -5
- {trigger-2.0.0 → trigger-2.0.3}/trigger/cmds.py +110 -126
- {trigger-2.0.0 → trigger-2.0.3}/trigger/conf/__init__.py +6 -7
- {trigger-2.0.0 → trigger-2.0.3}/trigger/conf/global_settings.py +35 -37
- {trigger-2.0.0 → trigger-2.0.3}/trigger/contrib/__init__.py +1 -2
- {trigger-2.0.0 → trigger-2.0.3}/trigger/exceptions.py +11 -21
- {trigger-2.0.0 → trigger-2.0.3}/trigger/gorc.py +12 -17
- {trigger-2.0.0 → trigger-2.0.3}/trigger/netdevices/__init__.py +84 -137
- {trigger-2.0.0 → trigger-2.0.3}/trigger/netdevices/loader.py +22 -30
- {trigger-2.0.0 → trigger-2.0.3}/trigger/netscreen.py +73 -98
- {trigger-2.0.0 → trigger-2.0.3}/trigger/rancid.py +51 -60
- {trigger-2.0.0 → trigger-2.0.3}/trigger/tacacsrc.py +72 -76
- {trigger-2.0.0 → trigger-2.0.3}/trigger/twister.py +165 -243
- {trigger-2.0.0 → trigger-2.0.3}/trigger/twister2.py +40 -57
- {trigger-2.0.0 → trigger-2.0.3}/trigger/utils/__init__.py +10 -17
- {trigger-2.0.0 → trigger-2.0.3}/trigger/utils/cli.py +27 -37
- {trigger-2.0.0 → trigger-2.0.3}/trigger/utils/importlib.py +18 -15
- {trigger-2.0.0 → trigger-2.0.3}/trigger/utils/network.py +17 -22
- {trigger-2.0.0 → trigger-2.0.3}/trigger/utils/rcs.py +14 -23
- {trigger-2.0.0 → trigger-2.0.3}/trigger/utils/templates.py +11 -14
- {trigger-2.0.0 → trigger-2.0.3}/trigger/utils/url.py +3 -7
- {trigger-2.0.0 → trigger-2.0.3}/trigger/utils/xmltodict.py +11 -10
- {trigger-2.0.0 → trigger-2.0.3/trigger.egg-info}/PKG-INFO +1 -1
- trigger-2.0.0/pyproject.toml +0 -156
- {trigger-2.0.0 → trigger-2.0.3}/AUTHORS.md +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/LICENSE.md +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/README.md +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/setup.cfg +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/tests/test_acl_db.py +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/tests/test_changemgmt.py +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/tests/test_twister.py +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/tests/test_utils.py +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/trigger/__init__.py +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/trigger/bin/__init__.py +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/trigger/packages/__init__.py +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/trigger/packages/peewee.py +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/trigger.egg-info/SOURCES.txt +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/trigger.egg-info/dependency_links.txt +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/trigger.egg-info/entry_points.txt +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/trigger.egg-info/requires.txt +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/trigger.egg-info/top_level.txt +0 -0
- {trigger-2.0.0 → trigger-2.0.3}/twisted/plugins/trigger_xmlrpc.py +0 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "trigger"
|
|
7
|
+
version = "2.0.3"
|
|
8
|
+
description = "Network automation toolkit for managing network devices"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "BSD-3-Clause"
|
|
11
|
+
authors = [{name = "Jathan McCollum", email = "jathan@gmail.com"}]
|
|
12
|
+
requires-python = ">=3.10,<3.12"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 6 - Mature",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Framework :: Twisted",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"IPy>=1.01",
|
|
22
|
+
"cryptography>=41.0.0",
|
|
23
|
+
"Twisted>=22.10.0",
|
|
24
|
+
"crochet>=2.0.0",
|
|
25
|
+
"pyasn1>=0.4.8",
|
|
26
|
+
"pyparsing>=3.1.0",
|
|
27
|
+
"pytz>=2023.3",
|
|
28
|
+
"SimpleParse>=2.2.0",
|
|
29
|
+
"textfsm>=1.1.0",
|
|
30
|
+
"redis>=5.0.0",
|
|
31
|
+
"PTable>=0.9.2",
|
|
32
|
+
"peewee>=3.17.0",
|
|
33
|
+
"service-identity>=23.1.0",
|
|
34
|
+
"bcrypt>=4.0.0",
|
|
35
|
+
"packaging>=21.0",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
dev = [
|
|
40
|
+
"pytest>=7.4.0",
|
|
41
|
+
"pytest-twisted>=1.14.0",
|
|
42
|
+
"ruff>=0.1.0",
|
|
43
|
+
"python-semantic-release>=9.0.0",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.scripts]
|
|
47
|
+
acl = "trigger.bin.acl:main"
|
|
48
|
+
acl_script = "trigger.bin.acl_script:main"
|
|
49
|
+
aclconv = "trigger.bin.aclconv:main"
|
|
50
|
+
check_access = "trigger.bin.check_access:main"
|
|
51
|
+
check_syntax = "trigger.bin.check_syntax:main"
|
|
52
|
+
fe = "trigger.bin.fe:main"
|
|
53
|
+
find_access = "trigger.bin.find_access:main"
|
|
54
|
+
gnng = "trigger.bin.gnng:main"
|
|
55
|
+
gong = "trigger.bin.gong:main"
|
|
56
|
+
load_acl = "trigger.bin.load_acl:main"
|
|
57
|
+
load_config = "trigger.bin.load_config:main"
|
|
58
|
+
netdev = "trigger.bin.netdev:main"
|
|
59
|
+
optimizer = "trigger.bin.optimizer:main"
|
|
60
|
+
run_cmds = "trigger.bin.run_cmds:main"
|
|
61
|
+
|
|
62
|
+
[tool.setuptools]
|
|
63
|
+
packages = ["trigger", "trigger.acl", "trigger.bin", "trigger.changemgmt",
|
|
64
|
+
"trigger.conf", "trigger.contrib", "trigger.netdevices",
|
|
65
|
+
"trigger.packages", "trigger.utils", "twisted.plugins"]
|
|
66
|
+
include-package-data = true
|
|
67
|
+
|
|
68
|
+
[tool.setuptools.package-data]
|
|
69
|
+
twisted = ["plugins/trigger_xmlrpc.py"]
|
|
70
|
+
|
|
71
|
+
# Automatically includes files tracked by git in source distributions
|
|
72
|
+
# This includes: CHANGELOG, *.md, conf/, docs/, examples/, tests/
|
|
73
|
+
|
|
74
|
+
[tool.pytest.ini_options]
|
|
75
|
+
testpaths = ["tests"]
|
|
76
|
+
python_files = ["test_*.py"]
|
|
77
|
+
addopts = "-v"
|
|
78
|
+
|
|
79
|
+
[tool.ruff]
|
|
80
|
+
line-length = 88
|
|
81
|
+
target-version = "py310"
|
|
82
|
+
exclude = ["trigger/packages", ".venv", "build", "dist"]
|
|
83
|
+
|
|
84
|
+
[tool.ruff.lint]
|
|
85
|
+
select = [
|
|
86
|
+
"B", # flake8-bugbear - bug prevention
|
|
87
|
+
"COM", # flake8-commas - trailing comma consistency
|
|
88
|
+
"D", # pydocstyle - docstring formatting
|
|
89
|
+
"E", # pycodestyle errors
|
|
90
|
+
"EM", # flake8-errmsg - exception message hygiene
|
|
91
|
+
"ERA", # eradicate - commented-out code removal
|
|
92
|
+
"F", # pyflakes
|
|
93
|
+
"FURB", # refurb - modern Python idioms
|
|
94
|
+
"I", # isort - import sorting
|
|
95
|
+
"PERF", # perflint - performance patterns
|
|
96
|
+
"PIE", # flake8-pie - unnecessary constructs
|
|
97
|
+
"PLR", # pylint-refactor
|
|
98
|
+
"PLW", # pylint-warnings
|
|
99
|
+
"PT", # flake8-pytest-style
|
|
100
|
+
"PTH", # flake8-use-pathlib
|
|
101
|
+
"RET", # flake8-return
|
|
102
|
+
"RSE", # flake8-raise
|
|
103
|
+
"RUF", # ruff-specific rules
|
|
104
|
+
"S", # flake8-bandit - security
|
|
105
|
+
"SIM", # flake8-simplify
|
|
106
|
+
"UP", # pyupgrade - Python 3 modernization
|
|
107
|
+
"W", # pycodestyle warnings
|
|
108
|
+
]
|
|
109
|
+
ignore = [
|
|
110
|
+
# --- Formatter conflicts ---
|
|
111
|
+
"COM812", # trailing-comma-missing (handled by ruff formatter)
|
|
112
|
+
|
|
113
|
+
# --- Kept from v2.0.0 baseline ---
|
|
114
|
+
"E402", # module-import-not-at-top-of-file
|
|
115
|
+
"E501", # line-too-long (handled by formatter)
|
|
116
|
+
"E721", # type-comparison (existing code style)
|
|
117
|
+
"E722", # bare-except (intentional in many places)
|
|
118
|
+
"E741", # ambiguous-variable-name (existing code style)
|
|
119
|
+
"F401", # unused-import (may be used dynamically)
|
|
120
|
+
"F402", # import-shadowed-by-loop-var
|
|
121
|
+
"F403", # undefined-local-with-import-star
|
|
122
|
+
"F405", # undefined-local-with-import-star-usage
|
|
123
|
+
"F523", # string-dot-format-extra-positional-arguments
|
|
124
|
+
"F811", # redefined-while-unused
|
|
125
|
+
"F821", # undefined-name
|
|
126
|
+
"UP031", # printf-string-formatting
|
|
127
|
+
|
|
128
|
+
# --- Bugbear exceptions ---
|
|
129
|
+
"B016", # raise-literal (used intentionally in tests)
|
|
130
|
+
"B018", # useless-expression (test assertions)
|
|
131
|
+
|
|
132
|
+
# --- Docstring exceptions (not enforcing coverage/style) ---
|
|
133
|
+
"D100", # undocumented-public-module
|
|
134
|
+
"D101", # undocumented-public-class
|
|
135
|
+
"D102", # undocumented-public-method
|
|
136
|
+
"D103", # undocumented-public-function
|
|
137
|
+
"D104", # undocumented-public-package
|
|
138
|
+
"D105", # undocumented-magic-method
|
|
139
|
+
"D107", # undocumented-public-init
|
|
140
|
+
"D200", # unnecessary-multiline-docstring
|
|
141
|
+
"D205", # missing-blank-line-after-summary
|
|
142
|
+
"D401", # non-imperative-mood
|
|
143
|
+
"D402", # signature-in-docstring
|
|
144
|
+
"D404", # docstring-starts-with-this
|
|
145
|
+
|
|
146
|
+
# --- Complexity (deep refactoring territory) ---
|
|
147
|
+
"PLR0911", # too-many-return-statements
|
|
148
|
+
"PLR0912", # too-many-branches
|
|
149
|
+
"PLR0913", # too-many-arguments
|
|
150
|
+
"PLR0915", # too-many-statements
|
|
151
|
+
"PLR2004", # magic-value-comparison
|
|
152
|
+
"PLW0603", # global-statement (parser pattern)
|
|
153
|
+
|
|
154
|
+
# --- Security false positives for network toolkit ---
|
|
155
|
+
"S101", # assert (used in tests)
|
|
156
|
+
"S105", # hardcoded-password-string (false positives)
|
|
157
|
+
"S110", # try-except-pass
|
|
158
|
+
"S112", # try-except-continue
|
|
159
|
+
"S314", # suspicious-xml-element-tree-usage
|
|
160
|
+
"S603", # subprocess-without-shell-equals-true
|
|
161
|
+
"S605", # start-process-with-a-shell (fix manually)
|
|
162
|
+
"S606", # start-process-with-no-shell
|
|
163
|
+
"S608", # hardcoded-sql-expression
|
|
164
|
+
|
|
165
|
+
# --- Other ---
|
|
166
|
+
"RUF012", # mutable-class-default (too many in device metadata)
|
|
167
|
+
"SLF001", # private-member-access (common in Twisted patterns)
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
[tool.ruff.lint.pydocstyle]
|
|
171
|
+
convention = "google"
|
|
172
|
+
|
|
173
|
+
[tool.ruff.lint.per-file-ignores]
|
|
174
|
+
"tests/**/*.py" = [
|
|
175
|
+
"ARG", # unused arguments (test fixtures)
|
|
176
|
+
"D", # docstrings not enforced in tests
|
|
177
|
+
"E402", # module-level imports not at top
|
|
178
|
+
"E722", # bare-except
|
|
179
|
+
"E731", # lambda assignments
|
|
180
|
+
"F821", # undefined names (test fixtures)
|
|
181
|
+
"PT009", # pytest-unittest-assertion (allow mixing in tests)
|
|
182
|
+
"PT027", # pytest-unittest-raises-assertion
|
|
183
|
+
"S", # security checks not relevant in tests
|
|
184
|
+
]
|
|
185
|
+
"tests/acceptance/**/*.py" = [
|
|
186
|
+
"D", # docstrings not enforced
|
|
187
|
+
"E402", # module-level imports not at top
|
|
188
|
+
"E722", # bare-except
|
|
189
|
+
"F821", # undefined names
|
|
190
|
+
"S", # security checks not relevant
|
|
191
|
+
]
|
|
192
|
+
"trigger/bin/**/*.py" = [
|
|
193
|
+
"S605", # start-process-with-a-shell (CLI tools)
|
|
194
|
+
"S606", # start-process-with-no-shell
|
|
195
|
+
"S607", # start-process-with-partial-path
|
|
196
|
+
]
|
|
197
|
+
"configs/**/*.py" = [
|
|
198
|
+
"ERA001", # commented-out code (serves as user documentation/examples)
|
|
199
|
+
"PTH", # pathlib (settings values are strings by design)
|
|
200
|
+
]
|
|
201
|
+
"docs/conf.py" = [
|
|
202
|
+
"D", # docstrings not enforced
|
|
203
|
+
"E402", # module-level imports not at top
|
|
204
|
+
"F401", # unused imports (used by Sphinx)
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
[tool.ruff.lint.isort]
|
|
208
|
+
known-first-party = ["trigger"]
|
|
209
|
+
|
|
210
|
+
[tool.ruff.format]
|
|
211
|
+
quote-style = "double"
|
|
212
|
+
indent-style = "space"
|
|
213
|
+
skip-magic-trailing-comma = false
|
|
214
|
+
line-ending = "auto"
|
|
215
|
+
|
|
216
|
+
[tool.semantic_release]
|
|
217
|
+
version_toml = ["pyproject.toml:project.version"]
|
|
218
|
+
branch = "main"
|
|
219
|
+
upload_to_pypi = true
|
|
220
|
+
upload_to_release = true
|
|
221
|
+
build_command = "pip install uv && uv build"
|
|
222
|
+
major_on_zero = false
|
|
223
|
+
tag_format = "v{version}"
|
|
224
|
+
|
|
225
|
+
[tool.semantic_release.commit_parser_options]
|
|
226
|
+
allowed_tags = ["feat", "fix", "perf", "refactor", "docs", "chore", "ci", "test", "build"]
|
|
227
|
+
major_tags = ["BREAKING CHANGE"]
|
|
228
|
+
minor_tags = ["feat"]
|
|
229
|
+
patch_tags = ["fix", "perf"]
|
|
230
|
+
|
|
231
|
+
[tool.semantic_release.changelog]
|
|
232
|
+
changelog_file = "CHANGELOG.md"
|
|
233
|
+
|
|
234
|
+
[tool.semantic_release.branches.main]
|
|
235
|
+
match = "main"
|
|
236
|
+
prerelease = false
|
|
@@ -5,8 +5,10 @@ __maintainer__ = "Jathan McCollum"
|
|
|
5
5
|
__copyright__ = "Copyright 2005-2011 AOL Inc.; 2013 Salesforce.com"
|
|
6
6
|
__version__ = "2.0"
|
|
7
7
|
|
|
8
|
+
import contextlib
|
|
8
9
|
import unittest
|
|
9
10
|
from io import StringIO
|
|
11
|
+
from pathlib import Path
|
|
10
12
|
|
|
11
13
|
from trigger import acl, exceptions
|
|
12
14
|
|
|
@@ -91,7 +93,7 @@ class CheckACLNames(unittest.TestCase):
|
|
|
91
93
|
for name in ("", "x" * 25):
|
|
92
94
|
try:
|
|
93
95
|
acl.ACL(name=name)
|
|
94
|
-
except exceptions.ACLNameError:
|
|
96
|
+
except exceptions.ACLNameError: # noqa: PERF203
|
|
95
97
|
pass
|
|
96
98
|
else:
|
|
97
99
|
self.fail('expected ACLNameError on "' + name + '"')
|
|
@@ -131,7 +133,7 @@ class CheckTerms(unittest.TestCase):
|
|
|
131
133
|
for name in ("", "x" * 300):
|
|
132
134
|
try:
|
|
133
135
|
acl.Term(name=name)
|
|
134
|
-
except exceptions.BadTermName:
|
|
136
|
+
except exceptions.BadTermName: # noqa: PERF203
|
|
135
137
|
pass
|
|
136
138
|
else:
|
|
137
139
|
self.fail('expected BadTermNameon "' + name + '"')
|
|
@@ -170,10 +172,10 @@ class CheckTerms(unittest.TestCase):
|
|
|
170
172
|
):
|
|
171
173
|
try:
|
|
172
174
|
acl.Term(action=action)
|
|
173
|
-
except exceptions.ActionError:
|
|
175
|
+
except exceptions.ActionError: # noqa: PERF203
|
|
174
176
|
pass
|
|
175
177
|
else:
|
|
176
|
-
self.fail(f'expected ActionError on "{
|
|
178
|
+
self.fail(f'expected ActionError on "{action!s}"')
|
|
177
179
|
|
|
178
180
|
def testOkModifiers(self):
|
|
179
181
|
"""Test valid filter action modifiers"""
|
|
@@ -210,10 +212,10 @@ class CheckTerms(unittest.TestCase):
|
|
|
210
212
|
):
|
|
211
213
|
try:
|
|
212
214
|
acl.Term(action=action)
|
|
213
|
-
except exceptions.ActionError:
|
|
215
|
+
except exceptions.ActionError: # noqa: PERF203
|
|
214
216
|
pass
|
|
215
217
|
else:
|
|
216
|
-
self.fail(f'expected ActionError on "{
|
|
218
|
+
self.fail(f'expected ActionError on "{action!s}"')
|
|
217
219
|
|
|
218
220
|
def testOkMatches(self):
|
|
219
221
|
"""Test valid match conditions"""
|
|
@@ -307,7 +309,7 @@ class CheckOutput(unittest.TestCase):
|
|
|
307
309
|
self.t2.match["protocol"] = ["tcp"]
|
|
308
310
|
self.t2.match["source-address"] = ["192.0.2.0/24"]
|
|
309
311
|
# Python 3: range() returns a range object, convert to list for concatenation
|
|
310
|
-
self.t2.match["destination-port"] = list(range(135, 139))
|
|
312
|
+
self.t2.match["destination-port"] = [*list(range(135, 139)), 445]
|
|
311
313
|
self.t2.action = "reject"
|
|
312
314
|
self.t2.modifiers["syslog"] = True
|
|
313
315
|
self.a.terms.append(self.t2)
|
|
@@ -346,10 +348,8 @@ filter 100j {
|
|
|
346
348
|
def testIOS(self):
|
|
347
349
|
"""Test conversion of ACLs and terms to IOS classic format"""
|
|
348
350
|
self.a.name = 100
|
|
349
|
-
|
|
351
|
+
with contextlib.suppress(KeyError):
|
|
350
352
|
del self.t1.modifiers["count"]
|
|
351
|
-
except KeyError:
|
|
352
|
-
pass
|
|
353
353
|
output = """\
|
|
354
354
|
access-list 100 permit 99 any any
|
|
355
355
|
access-list 100 deny tcp 192.0.2.0 0.0.0.255 any range 135 138 log
|
|
@@ -359,10 +359,8 @@ access-list 100 deny tcp 192.0.2.0 0.0.0.255 any eq 445 log"""
|
|
|
359
359
|
def testIOSExtended(self):
|
|
360
360
|
"""Test conversion of ACLs and terms to IOS extended format"""
|
|
361
361
|
self.a.name = "BLAHBLAH"
|
|
362
|
-
|
|
362
|
+
with contextlib.suppress(KeyError):
|
|
363
363
|
del self.t1.modifiers["count"]
|
|
364
|
-
except KeyError:
|
|
365
|
-
pass
|
|
366
364
|
output = """\
|
|
367
365
|
ip access-list extended BLAHBLAH
|
|
368
366
|
permit 99 any any
|
|
@@ -373,10 +371,8 @@ ip access-list extended BLAHBLAH
|
|
|
373
371
|
def testIOSXR(self):
|
|
374
372
|
"""Test conversion of ACLs and terms to IOS XR format"""
|
|
375
373
|
self.a.name = "BLAHBLAH"
|
|
376
|
-
|
|
374
|
+
with contextlib.suppress(KeyError):
|
|
377
375
|
del self.t1.modifiers["count"]
|
|
378
|
-
except KeyError:
|
|
379
|
-
pass
|
|
380
376
|
self.t1.name = self.t2.name = None
|
|
381
377
|
output = """\
|
|
382
378
|
ipv4 access-list BLAHBLAH
|
|
@@ -475,7 +471,7 @@ class CheckJunOSExamples(unittest.TestCase):
|
|
|
475
471
|
def testJunOSExamples(self):
|
|
476
472
|
"""Test examples from JunOS documentation."""
|
|
477
473
|
# Python 3: file() removed, use open()
|
|
478
|
-
with
|
|
474
|
+
with Path(EXAMPLES_FILE).open() as f:
|
|
479
475
|
examples = f.read().expandtabs().split("\n\n")
|
|
480
476
|
# Skip the last two because they use the unimplemented "except"
|
|
481
477
|
# feature in address matches.
|
|
@@ -602,10 +598,6 @@ filter 100 {
|
|
|
602
598
|
}
|
|
603
599
|
}"""
|
|
604
600
|
self.assertRaises(exceptions.ParserSyntaxError, lambda: acl.parse(x))
|
|
605
|
-
###y = ['access-list 100 permit tcp any any eq 80']
|
|
606
|
-
###a = acl.parse(x)
|
|
607
|
-
###a.comments = a.terms[0].comments = []
|
|
608
|
-
###self.assertEqual(a.output_ios(), y)
|
|
609
601
|
|
|
610
602
|
def testRanges(self):
|
|
611
603
|
"""Test JunOS ICMP and protocol ranges (regression)."""
|
|
@@ -702,7 +694,8 @@ class CheckMiscIOS(unittest.TestCase):
|
|
|
702
694
|
t.match["icmp-type"] = types
|
|
703
695
|
# Python 3: map() returns an iterator, convert to list for comparison
|
|
704
696
|
self.assertEqual(
|
|
705
|
-
t.output_ios(),
|
|
697
|
+
t.output_ios(),
|
|
698
|
+
list(map(lambda x: "permit icmp any any %d" % x, types)),
|
|
706
699
|
)
|
|
707
700
|
|
|
708
701
|
def testCounterSuppression(self):
|
|
@@ -8,6 +8,7 @@ Only tests SQLite for now.
|
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
10
|
import tempfile
|
|
11
|
+
from pathlib import Path
|
|
11
12
|
|
|
12
13
|
from trigger.conf import settings
|
|
13
14
|
|
|
@@ -79,7 +80,10 @@ class TestAclQueue(unittest.TestCase):
|
|
|
79
80
|
def test_03_insert_integrated_failure_acl(self):
|
|
80
81
|
"""Test insert devices w/ no ACL association"""
|
|
81
82
|
self.assertRaises(
|
|
82
|
-
exceptions.TriggerError,
|
|
83
|
+
exceptions.TriggerError,
|
|
84
|
+
self.q.insert,
|
|
85
|
+
"bogus",
|
|
86
|
+
self.device_list,
|
|
83
87
|
)
|
|
84
88
|
|
|
85
89
|
def test_04_list_integrated_success(self):
|
|
@@ -153,7 +157,7 @@ class TestAclQueue(unittest.TestCase):
|
|
|
153
157
|
|
|
154
158
|
def test_ZZ_cleanup_db(self):
|
|
155
159
|
"""Cleanup the temp database file"""
|
|
156
|
-
self.assertTrue(
|
|
160
|
+
self.assertTrue(Path(db_file).unlink() is None)
|
|
157
161
|
|
|
158
162
|
def tearDown(self):
|
|
159
163
|
NetDevices._Singleton = None
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# a test for except processing
|
|
2
2
|
import os
|
|
3
3
|
import sys
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
|
|
5
|
-
sys.path.append(
|
|
6
|
+
sys.path.append(str(Path(__file__).parent / ".."))
|
|
6
7
|
from trigger import acl
|
|
7
8
|
|
|
8
9
|
PARSIT = """
|
|
@@ -33,4 +34,3 @@ y = acl.parse(PARSIT)
|
|
|
33
34
|
print(y.terms[0].match)
|
|
34
35
|
print("\n".join(acl.parse(PARSIT).output_junos()))
|
|
35
36
|
# following should fail
|
|
36
|
-
# print('\n'.join(acl.parse(PARSIT).output_ios()))
|
|
@@ -129,8 +129,8 @@ class TestNetDevicesWithoutAcls(unittest.TestCase):
|
|
|
129
129
|
def setUp(self):
|
|
130
130
|
self.nd = NetDevices(with_acls=False)
|
|
131
131
|
# Python 3: dict.keys() and dict.values() return views, convert to list for subscripting
|
|
132
|
-
self.nodename =
|
|
133
|
-
self.device =
|
|
132
|
+
self.nodename = next(iter(self.nd.keys()))
|
|
133
|
+
self.device = next(iter(self.nd.values()))
|
|
134
134
|
|
|
135
135
|
def test_aclsdb(self):
|
|
136
136
|
"""Test acls.db handling."""
|
|
@@ -173,12 +173,10 @@ class TestNetDeviceObject(unittest.TestCase):
|
|
|
173
173
|
def test_allowable(self):
|
|
174
174
|
"""Test allowable() method"""
|
|
175
175
|
# This is already tested in test_changemgmt.py, so this is a stub.
|
|
176
|
-
pass
|
|
177
176
|
|
|
178
177
|
def test_next_ok(self):
|
|
179
178
|
"""Test next_ok() method"""
|
|
180
179
|
# This is already tested in test_changemgmt.py, so this is a stub.
|
|
181
|
-
pass
|
|
182
180
|
|
|
183
181
|
def test_identity(self):
|
|
184
182
|
"""Exercise NetDevice identity tests."""
|
|
@@ -207,7 +205,7 @@ class TestNetDeviceObject(unittest.TestCase):
|
|
|
207
205
|
|
|
208
206
|
def test_dump(self):
|
|
209
207
|
"""Test the dump() method."""
|
|
210
|
-
with captured_output() as (out,
|
|
208
|
+
with captured_output() as (out, _err):
|
|
211
209
|
self.device.dump()
|
|
212
210
|
expected = NETDEVICE_DUMP_EXPECTED
|
|
213
211
|
output = out.getvalue()
|
|
@@ -254,9 +252,9 @@ class TestVendorObject(unittest.TestCase):
|
|
|
254
252
|
def test_membership(self):
|
|
255
253
|
"""Test membership w/ __eq__ and __contains__"""
|
|
256
254
|
expected = "cisco"
|
|
257
|
-
self.assertTrue(expected
|
|
258
|
-
self.assertTrue(self.vendor
|
|
259
|
-
self.assertTrue(self.vendor
|
|
255
|
+
self.assertTrue(expected == self.vendor)
|
|
256
|
+
self.assertTrue(self.vendor == self.vendor)
|
|
257
|
+
self.assertTrue(self.vendor == expected)
|
|
260
258
|
self.assertFalse(self.vendor in ["juniper", "foundry"])
|
|
261
259
|
|
|
262
260
|
def test_determine_vendor(self):
|
|
@@ -10,10 +10,11 @@ __version__ = "1.1"
|
|
|
10
10
|
import os
|
|
11
11
|
import subprocess
|
|
12
12
|
import unittest
|
|
13
|
+
from pathlib import Path
|
|
13
14
|
|
|
14
15
|
ACLCONV = "aclconv" # Now an entry point, not a script in bin/
|
|
15
16
|
|
|
16
|
-
os.environ["PYTHONPATH"] =
|
|
17
|
+
os.environ["PYTHONPATH"] = str(Path.cwd())
|
|
17
18
|
|
|
18
19
|
# TODO (jathan): Add tests for all the scripts!!
|
|
19
20
|
|
|
@@ -30,7 +31,7 @@ class Aclconv(unittest.TestCase):
|
|
|
30
31
|
stderr=subprocess.PIPE,
|
|
31
32
|
text=True,
|
|
32
33
|
)
|
|
33
|
-
output,
|
|
34
|
+
output, _errors = proc.communicate("access-list 100 deny ip any any")
|
|
34
35
|
self.assertEqual(proc.returncode, 0)
|
|
35
36
|
correct_output = """\
|
|
36
37
|
firewall {
|
|
@@ -9,6 +9,7 @@ __version__ = "2.0.1"
|
|
|
9
9
|
import os
|
|
10
10
|
import tempfile
|
|
11
11
|
import unittest
|
|
12
|
+
from pathlib import Path
|
|
12
13
|
from unittest.mock import patch
|
|
13
14
|
|
|
14
15
|
from trigger.conf import settings
|
|
@@ -89,7 +90,7 @@ class TacacsrcTest(unittest.TestCase):
|
|
|
89
90
|
def _get_perms(self, filename):
|
|
90
91
|
"""Get octal permissions for a filename"""
|
|
91
92
|
# We only want the lower 4 bits (negative index)
|
|
92
|
-
return oct(
|
|
93
|
+
return oct(Path(filename).stat().st_mode)[-4:]
|
|
93
94
|
|
|
94
95
|
def testWrite(self):
|
|
95
96
|
"""Test writing .tacacsrc."""
|
|
@@ -110,26 +111,30 @@ class TacacsrcTest(unittest.TestCase):
|
|
|
110
111
|
self.assertEqual(output, ALL_TACACSRC)
|
|
111
112
|
|
|
112
113
|
# And then compare it against the manually parsed value using
|
|
113
|
-
# miniparser()
|
|
114
|
-
with
|
|
114
|
+
# miniparser() # noqa: ERA001
|
|
115
|
+
with Path(settings.TACACSRC).open() as fd:
|
|
115
116
|
lines = fd.readlines()
|
|
116
117
|
self.assertEqual(output, miniparser(lines, t))
|
|
117
|
-
|
|
118
|
+
Path(file_name).unlink()
|
|
118
119
|
|
|
119
120
|
def test_brokenpw(self):
|
|
120
121
|
self.assertRaises(
|
|
121
|
-
ValueError,
|
|
122
|
+
ValueError,
|
|
123
|
+
MockTacacsrc,
|
|
124
|
+
tacacsrc_file="tests/data/brokenpw_tacacsrc",
|
|
122
125
|
)
|
|
123
126
|
|
|
124
127
|
def test_emptypw(self):
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
with (
|
|
129
|
+
Path(os.devnull).open("w") as devnull,
|
|
130
|
+
patch("trigger.tacacsrc.prompt_credentials", side_effect=KeyError),
|
|
131
|
+
patch("sys.stdout", devnull),
|
|
132
|
+
):
|
|
133
|
+
self.assertRaises(
|
|
134
|
+
KeyError,
|
|
135
|
+
MockTacacsrc,
|
|
136
|
+
tacacsrc_file="tests/data/emptypw_tacacsrc",
|
|
137
|
+
)
|
|
133
138
|
|
|
134
139
|
def test_perms(self):
|
|
135
140
|
"""Test that permissions are being enforced."""
|
|
@@ -138,7 +143,7 @@ class TacacsrcTest(unittest.TestCase):
|
|
|
138
143
|
# First make sure perms are set
|
|
139
144
|
old_perms = self._get_perms(fname)
|
|
140
145
|
self.assertEqual(old_perms, RIGHT_PERMS)
|
|
141
|
-
|
|
146
|
+
Path(fname).chmod(0o666) # Make it world-writable
|
|
142
147
|
new_perms = self._get_perms(fname)
|
|
143
148
|
self.assertNotEqual(new_perms, RIGHT_PERMS)
|
|
144
149
|
|
|
@@ -128,7 +128,9 @@ class CheckTemplates(unittest.TestCase):
|
|
|
128
128
|
commands = ["show version"]
|
|
129
129
|
commando = Commando(devices=[self.device.nodeName])
|
|
130
130
|
data = commando.parse_template(
|
|
131
|
-
results=[big_cli_data],
|
|
131
|
+
results=[big_cli_data],
|
|
132
|
+
device=self.device,
|
|
133
|
+
commands=commands,
|
|
132
134
|
)
|
|
133
135
|
self.assertTrue(len(data) > 0)
|
|
134
136
|
self.assertTrue(isinstance(data, list))
|
|
@@ -143,7 +145,9 @@ class CheckTemplates(unittest.TestCase):
|
|
|
143
145
|
commands = ["show run | in cisco"]
|
|
144
146
|
commando = Commando(devices=[self.device.nodeName])
|
|
145
147
|
data = commando.parse_template(
|
|
146
|
-
results=[no_template_data],
|
|
148
|
+
results=[no_template_data],
|
|
149
|
+
device=self.device,
|
|
150
|
+
commands=commands,
|
|
147
151
|
)
|
|
148
152
|
self.assertTrue(len(data) > 0)
|
|
149
153
|
self.assertTrue(isinstance(data, list))
|
|
@@ -13,8 +13,4 @@ __copyright__ = "Copyright 2005-2011 AOL Inc.; 2013 Salesforce.com.; 2016 Dropbo
|
|
|
13
13
|
__version__ = "1.0"
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
# from utils import captured_output
|
|
17
|
-
|
|
18
|
-
# Now we can import from Trigger
|
|
19
|
-
# from trigger.netdevices import NetDevices, NetDevice, Vendor
|
|
20
16
|
# TODO: see http://twistedmatrix.com/trac/wiki/TwistedTrial
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Trigger's ACL parser.
|
|
1
|
+
"""Trigger's ACL parser.
|
|
3
2
|
|
|
4
3
|
This library contains various modules that allow for parsing, manipulation,
|
|
5
4
|
and management of network access control lists (ACLs). It will parse a complete
|
|
@@ -18,7 +17,7 @@ import os
|
|
|
18
17
|
|
|
19
18
|
from trigger.conf import settings
|
|
20
19
|
|
|
21
|
-
__all__ = ["
|
|
20
|
+
__all__ = ["acl_exists", "parser"]
|
|
22
21
|
|
|
23
22
|
# Parser
|
|
24
23
|
from . import parser
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
This module controls when ACLs get auto-applied to network devices,
|
|
1
|
+
"""This module controls when ACLs get auto-applied to network devices,
|
|
3
2
|
in addition to what is specified in acls.db.
|
|
4
3
|
|
|
5
4
|
This is primarily used by :class:`~trigger.acl.db.AclsDB` to populate the
|
|
@@ -44,11 +43,10 @@ try:
|
|
|
44
43
|
from _autoacl_module import autoacl
|
|
45
44
|
except ImportError:
|
|
46
45
|
msg = f"Function autoacl() could not be found in {module_path}, using default!"
|
|
47
|
-
warnings.warn(msg, RuntimeWarning)
|
|
46
|
+
warnings.warn(msg, RuntimeWarning, stacklevel=2)
|
|
48
47
|
|
|
49
48
|
def autoacl(dev, explicit_acls=None):
|
|
50
|
-
"""
|
|
51
|
-
Given a NetDevice object, returns a set of **implicit** (auto) ACLs. We
|
|
49
|
+
"""Given a NetDevice object, returns a set of **implicit** (auto) ACLs. We
|
|
52
50
|
require a device object so that we don't have circular dependencies
|
|
53
51
|
between netdevices and autoacl.
|
|
54
52
|
|