├── .bandit.yml ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .pylintrc ├── .yamllint.yml ├── 00_setup_netbox_devices.yml ├── 01_setup_devices.yml ├── 02_setup_interfaces.yml ├── 03_audit_netbox.yml ├── 04_setup_vm_clusters.yml ├── 2020-04-Ansible_Minneapolis_Meetup.pdf ├── 99_clean_env.yml ├── Dockerfile ├── Makefile ├── README.md ├── ansible.cfg ├── group_vars ├── all.yml ├── eos.yml ├── nxos.yml ├── platforms_edgeos.yml ├── platforms_eos.yml ├── platforms_ios.yml ├── platforms_nxos.yml ├── routers.yml └── switches.yml ├── host_vars ├── veos01.yml └── veos02.yml ├── netbox_inventory.yml ├── plugins └── filter │ └── filter.py ├── pytest.ini ├── requirements.system ├── requirements.txt ├── requirements.yml ├── tests ├── pb.unittest.yml ├── tasks │ ├── test_task.unittest.macaddress.yml │ └── test_task.unittest.netbox_device_compare.yml ├── test_build_ipv4_from_facts.py ├── test_compare_list.py ├── test_get_ciscoios_serial_list.py ├── test_get_role_from_hostname.py └── test_mac_address.py └── urls.md /.bandit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | skips: ["B101"] 3 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Build Container 17 | run: make build 18 | - name: Linting 19 | run: make lint 20 | - name: Unit Testing - Ansible 21 | run: make unit 22 | - name: Unit Testing Pytest 23 | run: make unit-pytest 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Exclude env files 2 | *.env 3 | 4 | # Created by https://www.gitignore.io/api/python,ansible,sublimetext,visualstudiocode 5 | # Edit at https://www.gitignore.io/?templates=python,ansible,sublimetext,visualstudiocode 6 | 7 | ### Ansible ### 8 | *.retry 9 | 10 | ### Python ### 11 | # Byte-compiled / optimized / DLL files 12 | __pycache__/ 13 | *.py[cod] 14 | *$py.class 15 | 16 | # C extensions 17 | *.so 18 | 19 | # Distribution / packaging 20 | .Python 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | downloads/ 25 | eggs/ 26 | .eggs/ 27 | lib/ 28 | lib64/ 29 | parts/ 30 | sdist/ 31 | var/ 32 | wheels/ 33 | pip-wheel-metadata/ 34 | share/python-wheels/ 35 | *.egg-info/ 36 | .installed.cfg 37 | *.egg 38 | MANIFEST 39 | 40 | # PyInstaller 41 | # Usually these files are written by a python script from a template 42 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 43 | *.manifest 44 | *.spec 45 | 46 | # Installer logs 47 | pip-log.txt 48 | pip-delete-this-directory.txt 49 | 50 | # Unit test / coverage reports 51 | htmlcov/ 52 | .tox/ 53 | .nox/ 54 | .coverage 55 | .coverage.* 56 | .cache 57 | nosetests.xml 58 | coverage.xml 59 | *.cover 60 | .hypothesis/ 61 | .pytest_cache/ 62 | 63 | # Translations 64 | *.mo 65 | *.pot 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # pipenv 80 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 81 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 82 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 83 | # install all needed dependencies. 84 | #Pipfile.lock 85 | 86 | # celery beat schedule file 87 | celerybeat-schedule 88 | 89 | # SageMath parsed files 90 | *.sage.py 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | .spyproject 95 | 96 | # Rope project settings 97 | .ropeproject 98 | 99 | # Mr Developer 100 | .mr.developer.cfg 101 | .project 102 | .pydevproject 103 | 104 | # mkdocs documentation 105 | /site 106 | 107 | # mypy 108 | .mypy_cache/ 109 | .dmypy.json 110 | dmypy.json 111 | 112 | # Pyre type checker 113 | .pyre/ 114 | 115 | ### SublimeText ### 116 | # Cache files for Sublime Text 117 | *.tmlanguage.cache 118 | *.tmPreferences.cache 119 | *.stTheme.cache 120 | 121 | # Workspace files are user-specific 122 | *.sublime-workspace 123 | 124 | # Project files should be checked into the repository, unless a significant 125 | # proportion of contributors will probably not be using Sublime Text 126 | # *.sublime-project 127 | 128 | # SFTP configuration file 129 | sftp-config.json 130 | 131 | # Package control specific files 132 | Package Control.last-run 133 | Package Control.ca-list 134 | Package Control.ca-bundle 135 | Package Control.system-ca-bundle 136 | Package Control.cache/ 137 | Package Control.ca-certs/ 138 | Package Control.merged-ca-bundle 139 | Package Control.user-ca-bundle 140 | oscrypto-ca-bundle.crt 141 | bh_unicode_properties.cache 142 | 143 | # Sublime-github package stores a github token in this file 144 | # https://packagecontrol.io/packages/sublime-github 145 | GitHub.sublime-settings 146 | 147 | ### VisualStudioCode ### 148 | .vscode/* 149 | !.vscode/settings.json 150 | !.vscode/tasks.json 151 | !.vscode/launch.json 152 | !.vscode/extensions.json 153 | 154 | ### VisualStudioCode Patch ### 155 | # Ignore all local history of files 156 | .history 157 | 158 | # End of https://www.gitignore.io/api/python,ansible,sublimetext,visualstudiocode -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # A comma-separated list of package or module names from where C extensions may 4 | # be loaded. Extensions are loading into the active Python interpreter and may 5 | # run arbitrary code. 6 | extension-pkg-whitelist= 7 | 8 | # Add files or directories to the blacklist. They should be base names, not 9 | # paths. 10 | ignore=CVS, 11 | CapitalNamesRule.py, 12 | test_get_ciscoios_serial_list.py 13 | 14 | # Add files or directories matching the regex patterns to the blacklist. The 15 | # regex matches against base names, not paths. 16 | ignore-patterns= 17 | 18 | # Python code to execute, usually for sys.path manipulation such as 19 | # pygtk.require(). 20 | #init-hook= 21 | 22 | # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the 23 | # number of processors available to use. 24 | jobs=1 25 | 26 | # Control the amount of potential inferred values when inferring a single 27 | # object. This can help the performance when dealing with large functions or 28 | # complex, nested conditions. 29 | limit-inference-results=100 30 | 31 | # List of plugins (as comma separated values of python module names) to load, 32 | # usually to register additional checkers. 33 | load-plugins= 34 | 35 | # Pickle collected data for later comparisons. 36 | persistent=yes 37 | 38 | # Specify a configuration file. 39 | #rcfile= 40 | 41 | # When enabled, pylint would attempt to guess common misconfiguration and emit 42 | # user-friendly hints instead of false-positive error messages. 43 | suggestion-mode=yes 44 | 45 | # Allow loading of arbitrary C extensions. Extensions are imported into the 46 | # active Python interpreter and may run arbitrary code. 47 | unsafe-load-any-extension=no 48 | 49 | 50 | [MESSAGES CONTROL] 51 | 52 | # Only show warnings with the listed confidence levels. Leave empty to show 53 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. 54 | confidence= 55 | 56 | # Disable the message, report, category or checker with the given id(s). You 57 | # can either give multiple identifiers separated by comma (,) or put this 58 | # option multiple times (only on the command line, not in the configuration 59 | # file where it should appear only once). You can also use "--disable=all" to 60 | # disable everything first and then reenable specific checks. For example, if 61 | # you want to run only the similarities checker, you can use "--disable=all 62 | # --enable=similarities". If you want to run only the classes checker, but have 63 | # no Warning level messages displayed, use "--disable=all --enable=classes 64 | # --disable=W". 65 | disable=print-statement, 66 | parameter-unpacking, 67 | unpacking-in-except, 68 | old-raise-syntax, 69 | backtick, 70 | long-suffix, 71 | old-ne-operator, 72 | old-octal-literal, 73 | import-star-module-level, 74 | non-ascii-bytes-literal, 75 | raw-checker-failed, 76 | bad-inline-option, 77 | locally-disabled, 78 | file-ignored, 79 | suppressed-message, 80 | useless-suppression, 81 | deprecated-pragma, 82 | use-symbolic-message-instead, 83 | apply-builtin, 84 | basestring-builtin, 85 | buffer-builtin, 86 | cmp-builtin, 87 | coerce-builtin, 88 | execfile-builtin, 89 | file-builtin, 90 | long-builtin, 91 | raw_input-builtin, 92 | reduce-builtin, 93 | standarderror-builtin, 94 | unicode-builtin, 95 | xrange-builtin, 96 | coerce-method, 97 | delslice-method, 98 | getslice-method, 99 | setslice-method, 100 | no-absolute-import, 101 | old-division, 102 | dict-iter-method, 103 | dict-view-method, 104 | next-method-called, 105 | metaclass-assignment, 106 | indexing-exception, 107 | raising-string, 108 | reload-builtin, 109 | oct-method, 110 | hex-method, 111 | nonzero-method, 112 | cmp-method, 113 | input-builtin, 114 | round-builtin, 115 | intern-builtin, 116 | unichr-builtin, 117 | map-builtin-not-iterating, 118 | zip-builtin-not-iterating, 119 | range-builtin-not-iterating, 120 | filter-builtin-not-iterating, 121 | using-cmp-argument, 122 | eq-without-hash, 123 | div-method, 124 | idiv-method, 125 | rdiv-method, 126 | exception-message-attribute, 127 | invalid-str-codec, 128 | sys-max-int, 129 | bad-python3-import, 130 | deprecated-string-function, 131 | deprecated-str-translate-call, 132 | deprecated-itertools-function, 133 | deprecated-types-field, 134 | next-method-defined, 135 | dict-items-not-iterating, 136 | dict-keys-not-iterating, 137 | dict-values-not-iterating, 138 | deprecated-operator-function, 139 | deprecated-urllib-function, 140 | xreadlines-attribute, 141 | deprecated-sys-function, 142 | exception-escape, 143 | comprehension-escape, 144 | import-error, 145 | too-many-lines, 146 | too-many-return-statements 147 | 148 | # Enable the message, report, category or checker with the given id(s). You can 149 | # either give multiple identifier separated by comma (,) or put this option 150 | # multiple time (only on the command line, not in the configuration file where 151 | # it should appear only once). See also the "--disable" option for examples. 152 | enable=c-extension-no-member 153 | 154 | 155 | [REPORTS] 156 | 157 | # Python expression which should return a score less than or equal to 10. You 158 | # have access to the variables 'error', 'warning', 'refactor', and 'convention' 159 | # which contain the number of messages in each category, as well as 'statement' 160 | # which is the total number of statements analyzed. This score is used by the 161 | # global evaluation report (RP0004). 162 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 163 | 164 | # Template used to display messages. This is a python new-style format string 165 | # used to format the message information. See doc for all details. 166 | #msg-template= 167 | 168 | # Set the output format. Available formats are text, parseable, colorized, json 169 | # and msvs (visual studio). You can also give a reporter class, e.g. 170 | # mypackage.mymodule.MyReporterClass. 171 | output-format=text 172 | 173 | # Tells whether to display a full report or only the messages. 174 | reports=no 175 | 176 | # Activate the evaluation score. 177 | score=yes 178 | 179 | 180 | [REFACTORING] 181 | 182 | # Maximum number of nested blocks for function / method body 183 | max-nested-blocks=5 184 | 185 | # Complete name of functions that never returns. When checking for 186 | # inconsistent-return-statements if a never returning function is called then 187 | # it will be considered as an explicit return statement and no message will be 188 | # printed. 189 | never-returning-functions=sys.exit 190 | 191 | 192 | [LOGGING] 193 | 194 | # Format style used to check logging format string. `old` means using % 195 | # formatting, `new` is for `{}` formatting,and `fstr` is for f-strings. 196 | logging-format-style=old 197 | 198 | # Logging modules to check that the string format arguments are in logging 199 | # function parameter format. 200 | logging-modules=logging 201 | 202 | 203 | [SPELLING] 204 | 205 | # Limits count of emitted suggestions for spelling mistakes. 206 | max-spelling-suggestions=4 207 | 208 | # Spelling dictionary name. Available dictionaries: none. To make it work, 209 | # install the python-enchant package. 210 | spelling-dict= 211 | 212 | # List of comma separated words that should not be checked. 213 | spelling-ignore-words= 214 | 215 | # A path to a file that contains the private dictionary; one word per line. 216 | spelling-private-dict-file= 217 | 218 | # Tells whether to store unknown words to the private dictionary (see the 219 | # --spelling-private-dict-file option) instead of raising a message. 220 | spelling-store-unknown-words=no 221 | 222 | 223 | [MISCELLANEOUS] 224 | 225 | # List of note tags to take in consideration, separated by a comma. 226 | notes=FIXME, 227 | XXX, 228 | TODO 229 | 230 | 231 | [TYPECHECK] 232 | 233 | # List of decorators that produce context managers, such as 234 | # contextlib.contextmanager. Add to this list to register other decorators that 235 | # produce valid context managers. 236 | contextmanager-decorators=contextlib.contextmanager 237 | 238 | # List of members which are set dynamically and missed by pylint inference 239 | # system, and so shouldn't trigger E1101 when accessed. Python regular 240 | # expressions are accepted. 241 | generated-members= 242 | 243 | # Tells whether missing members accessed in mixin class should be ignored. A 244 | # mixin class is detected if its name ends with "mixin" (case insensitive). 245 | ignore-mixin-members=yes 246 | 247 | # Tells whether to warn about missing members when the owner of the attribute 248 | # is inferred to be None. 249 | ignore-none=yes 250 | 251 | # This flag controls whether pylint should warn about no-member and similar 252 | # checks whenever an opaque object is returned when inferring. The inference 253 | # can return multiple potential results while evaluating a Python object, but 254 | # some branches might not be evaluated, which results in partial inference. In 255 | # that case, it might be useful to still emit no-member and other checks for 256 | # the rest of the inferred objects. 257 | ignore-on-opaque-inference=yes 258 | 259 | # List of class names for which member attributes should not be checked (useful 260 | # for classes with dynamically set attributes). This supports the use of 261 | # qualified names. 262 | ignored-classes=optparse.Values,thread._local,_thread._local 263 | 264 | # List of module names for which member attributes should not be checked 265 | # (useful for modules/projects where namespaces are manipulated during runtime 266 | # and thus existing member attributes cannot be deduced by static analysis). It 267 | # supports qualified module names, as well as Unix pattern matching. 268 | ignored-modules= 269 | 270 | # Show a hint with possible names when a member name was not found. The aspect 271 | # of finding the hint is based on edit distance. 272 | missing-member-hint=yes 273 | 274 | # The minimum edit distance a name should have in order to be considered a 275 | # similar match for a missing member name. 276 | missing-member-hint-distance=1 277 | 278 | # The total number of similar names that should be taken in consideration when 279 | # showing a hint for a missing member. 280 | missing-member-max-choices=1 281 | 282 | # List of decorators that change the signature of a decorated function. 283 | signature-mutators= 284 | 285 | 286 | [VARIABLES] 287 | 288 | # List of additional names supposed to be defined in builtins. Remember that 289 | # you should avoid defining new builtins when possible. 290 | additional-builtins= 291 | 292 | # Tells whether unused global variables should be treated as a violation. 293 | allow-global-unused-variables=yes 294 | 295 | # List of strings which can identify a callback function by name. A callback 296 | # name must start or end with one of those strings. 297 | callbacks=cb_, 298 | _cb 299 | 300 | # A regular expression matching the name of dummy variables (i.e. expected to 301 | # not be used). 302 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 303 | 304 | # Argument names that match this expression will be ignored. Default to name 305 | # with leading underscore. 306 | ignored-argument-names=_.*|^ignored_|^unused_ 307 | 308 | # Tells whether we should check for unused import in __init__ files. 309 | init-import=no 310 | 311 | # List of qualified module names which can have objects that can redefine 312 | # builtins. 313 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io 314 | 315 | 316 | [FORMAT] 317 | 318 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 319 | expected-line-ending-format= 320 | 321 | # Regexp for a line that is allowed to be longer than the limit. 322 | ignore-long-lines=^\s*(# )??$ 323 | 324 | # Number of spaces of indent required inside a hanging or continued line. 325 | indent-after-paren=4 326 | 327 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 328 | # tab). 329 | indent-string=' ' 330 | 331 | # Maximum number of characters on a single line. 332 | max-line-length=120 333 | 334 | # Maximum number of lines in a module. 335 | max-module-lines=1000 336 | 337 | # List of optional constructs for which whitespace checking is disabled. `dict- 338 | # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. 339 | # `trailing-comma` allows a space between comma and closing bracket: (a, ). 340 | # `empty-line` allows space-only lines. 341 | no-space-check=trailing-comma, 342 | dict-separator 343 | 344 | # Allow the body of a class to be on the same line as the declaration if body 345 | # contains single statement. 346 | single-line-class-stmt=no 347 | 348 | # Allow the body of an if to be on the same line as the test if there is no 349 | # else. 350 | single-line-if-stmt=no 351 | 352 | 353 | [SIMILARITIES] 354 | 355 | # Ignore comments when computing similarities. 356 | ignore-comments=yes 357 | 358 | # Ignore docstrings when computing similarities. 359 | ignore-docstrings=yes 360 | 361 | # Ignore imports when computing similarities. 362 | ignore-imports=no 363 | 364 | # Minimum lines number of a similarity. 365 | min-similarity-lines=4 366 | 367 | 368 | [BASIC] 369 | 370 | # Naming style matching correct argument names. 371 | argument-naming-style=snake_case 372 | 373 | # Regular expression matching correct argument names. Overrides argument- 374 | # naming-style. 375 | #argument-rgx= 376 | 377 | # Naming style matching correct attribute names. 378 | attr-naming-style=snake_case 379 | 380 | # Regular expression matching correct attribute names. Overrides attr-naming- 381 | # style. 382 | #attr-rgx= 383 | 384 | # Bad variable names which should always be refused, separated by a comma. 385 | bad-names=foo, 386 | bar, 387 | baz, 388 | toto, 389 | tutu, 390 | tata 391 | 392 | # Naming style matching correct class attribute names. 393 | class-attribute-naming-style=any 394 | 395 | # Regular expression matching correct class attribute names. Overrides class- 396 | # attribute-naming-style. 397 | #class-attribute-rgx= 398 | 399 | # Naming style matching correct class names. 400 | class-naming-style=PascalCase 401 | 402 | # Regular expression matching correct class names. Overrides class-naming- 403 | # style. 404 | #class-rgx= 405 | 406 | # Naming style matching correct constant names. 407 | const-naming-style=UPPER_CASE 408 | 409 | # Regular expression matching correct constant names. Overrides const-naming- 410 | # style. 411 | #const-rgx= 412 | 413 | # Minimum line length for functions/classes that require docstrings, shorter 414 | # ones are exempt. 415 | docstring-min-length=-1 416 | 417 | # Naming style matching correct function names. 418 | function-naming-style=snake_case 419 | 420 | # Regular expression matching correct function names. Overrides function- 421 | # naming-style. 422 | #function-rgx= 423 | 424 | # Good variable names which should always be accepted, separated by a comma. 425 | good-names=i, 426 | j, 427 | k, 428 | ex, 429 | Run, 430 | _ 431 | 432 | # Include a hint for the correct naming format with invalid-name. 433 | include-naming-hint=no 434 | 435 | # Naming style matching correct inline iteration names. 436 | inlinevar-naming-style=any 437 | 438 | # Regular expression matching correct inline iteration names. Overrides 439 | # inlinevar-naming-style. 440 | #inlinevar-rgx= 441 | 442 | # Naming style matching correct method names. 443 | method-naming-style=snake_case 444 | 445 | # Regular expression matching correct method names. Overrides method-naming- 446 | # style. 447 | #method-rgx= 448 | 449 | # Naming style matching correct module names. 450 | module-naming-style=snake_case 451 | 452 | # Regular expression matching correct module names. Overrides module-naming- 453 | # style. 454 | #module-rgx= 455 | 456 | # Colon-delimited sets of names that determine each other's naming style when 457 | # the name regexes allow several styles. 458 | name-group= 459 | 460 | # Regular expression which should only match function or class names that do 461 | # not require a docstring. 462 | no-docstring-rgx=^_ 463 | 464 | # List of decorators that produce properties, such as abc.abstractproperty. Add 465 | # to this list to register other decorators that produce valid properties. 466 | # These decorators are taken in consideration only for invalid-name. 467 | property-classes=abc.abstractproperty 468 | 469 | # Naming style matching correct variable names. 470 | variable-naming-style=snake_case 471 | 472 | # Regular expression matching correct variable names. Overrides variable- 473 | # naming-style. 474 | #variable-rgx= 475 | 476 | 477 | [STRING] 478 | 479 | # This flag controls whether the implicit-str-concat-in-sequence should 480 | # generate a warning on implicit string concatenation in sequences defined over 481 | # several lines. 482 | check-str-concat-over-line-jumps=no 483 | 484 | 485 | [IMPORTS] 486 | 487 | # List of modules that can be imported at any level, not just the top level 488 | # one. 489 | allow-any-import-level= 490 | 491 | # Allow wildcard imports from modules that define __all__. 492 | allow-wildcard-with-all=no 493 | 494 | # Analyse import fallback blocks. This can be used to support both Python 2 and 495 | # 3 compatible code, which means that the block might have code that exists 496 | # only in one or another interpreter, leading to false positives when analysed. 497 | analyse-fallback-blocks=no 498 | 499 | # Deprecated modules which should not be used, separated by a comma. 500 | deprecated-modules=optparse,tkinter.tix 501 | 502 | # Create a graph of external dependencies in the given file (report RP0402 must 503 | # not be disabled). 504 | ext-import-graph= 505 | 506 | # Create a graph of every (i.e. internal and external) dependencies in the 507 | # given file (report RP0402 must not be disabled). 508 | import-graph= 509 | 510 | # Create a graph of internal dependencies in the given file (report RP0402 must 511 | # not be disabled). 512 | int-import-graph= 513 | 514 | # Force import order to recognize a module as part of the standard 515 | # compatibility libraries. 516 | known-standard-library= 517 | 518 | # Force import order to recognize a module as part of a third party library. 519 | known-third-party=enchant 520 | 521 | # Couples of modules and preferred modules, separated by a comma. 522 | preferred-modules= 523 | 524 | 525 | [CLASSES] 526 | 527 | # List of method names used to declare (i.e. assign) instance attributes. 528 | defining-attr-methods=__init__, 529 | __new__, 530 | setUp, 531 | __post_init__ 532 | 533 | # List of member names, which should be excluded from the protected access 534 | # warning. 535 | exclude-protected=_asdict, 536 | _fields, 537 | _replace, 538 | _source, 539 | _make 540 | 541 | # List of valid names for the first argument in a class method. 542 | valid-classmethod-first-arg=cls 543 | 544 | # List of valid names for the first argument in a metaclass class method. 545 | valid-metaclass-classmethod-first-arg=cls 546 | 547 | 548 | [DESIGN] 549 | 550 | # Maximum number of arguments for function / method. 551 | max-args=5 552 | 553 | # Maximum number of attributes for a class (see R0902). 554 | max-attributes=7 555 | 556 | # Maximum number of boolean expressions in an if statement (see R0916). 557 | max-bool-expr=5 558 | 559 | # Maximum number of branch for function / method body. 560 | max-branches=12 561 | 562 | # Maximum number of locals for function / method body. 563 | max-locals=15 564 | 565 | # Maximum number of parents for a class (see R0901). 566 | max-parents=7 567 | 568 | # Maximum number of public methods for a class (see R0904). 569 | max-public-methods=20 570 | 571 | # Maximum number of return / yield for function / method body. 572 | max-returns=6 573 | 574 | # Maximum number of statements in function / method body. 575 | max-statements=50 576 | 577 | # Minimum number of public methods for a class (see R0903). 578 | min-public-methods=2 579 | 580 | 581 | [EXCEPTIONS] 582 | 583 | # Exceptions that will emit a warning when being caught. Defaults to 584 | # "BaseException, Exception". 585 | overgeneral-exceptions=BaseException, 586 | Exception 587 | -------------------------------------------------------------------------------- /.yamllint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | truthy: disable 6 | line-length: 7 | allow-non-breakable-inline-mappings: true 8 | allow-non-breakable-words: true 9 | max: 220 10 | brackets: 11 | min-spaces-inside: 0 12 | max-spaces-inside: 1 13 | braces: 14 | min-spaces-inside: 0 15 | max-spaces-inside: 1 16 | document-start: disable 17 | ... 18 | -------------------------------------------------------------------------------- /00_setup_netbox_devices.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "PLAY 1: SETUP DEVICES WITHIN NETBOX" 3 | hosts: localhost 4 | connection: local 5 | vars: 6 | install_state: present 7 | tasks: 8 | - name: "TASK 1: SETUP SITES" 9 | netbox.netbox.netbox_site: 10 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 11 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 12 | data: "{{ site }}" 13 | state: "{{ install_state }}" 14 | register: site_setup 15 | loop: "{{ sites }}" 16 | loop_control: 17 | loop_var: site 18 | label: "{{ site['name'] }}" 19 | tags: [ sites, devices ] 20 | 21 | - name: "TASK 2: SETUP RACKS" 22 | netbox.netbox.netbox_rack: 23 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 24 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 25 | data: "{{ rack }}" 26 | state: "{{ install_state }}" 27 | loop: "{{ racks }}" 28 | loop_control: 29 | loop_var: rack 30 | label: "{{ rack['name'] }}" 31 | tags: [ sites, devices ] 32 | 33 | - name: "TASK 3: SETUP MANUFACTURERS" 34 | netbox.netbox.netbox_manufacturer: 35 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 36 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 37 | data: 38 | name: "{{ manufacturer }}" 39 | state: "{{ install_state }}" 40 | loop: "{{ manufacturers }}" 41 | loop_control: 42 | loop_var: manufacturer 43 | tags: [ devices ] 44 | 45 | - name: "TASK 4: SETUP DEVICE TYPES" 46 | netbox.netbox.netbox_device_type: 47 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 48 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 49 | data: 50 | model: "{{ device_type.model }}" 51 | manufacturer: "{{ device_type.manufacturer }}" 52 | slug: "{{ device_type.slug }}" 53 | part_number: "{{ device_type.part_number }}" 54 | u_height: 1 55 | is_full_depth: "{{ device_type.full_depth }}" 56 | state: "{{ install_state }}" 57 | loop: "{{ device_types }}" 58 | loop_control: 59 | loop_var: device_type 60 | label: "{{ device_type['model'] }}" 61 | tags: [ devices ] 62 | 63 | - name: "TASK 5: SETUP PLATFORMS" 64 | netbox.netbox.netbox_platform: 65 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 66 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 67 | data: 68 | name: "{{ platform.name }}" 69 | slug: "{{ platform.slug }}" 70 | state: "{{ install_state }}" 71 | loop: "{{ platforms }}" 72 | loop_control: 73 | loop_var: platform 74 | label: "{{ platform['name'] }}" 75 | tags: [ devices ] 76 | 77 | - name: "TASK 6: SETUP DEVICE ROLES" 78 | netbox.netbox.netbox_device_role: 79 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 80 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 81 | data: 82 | name: "{{ device_role.name }}" 83 | color: "{{ device_role.color }}" 84 | vm_role: "{{ device_role.vmrole }}" 85 | state: "{{ install_state }}" 86 | loop: "{{ device_roles }}" 87 | loop_control: 88 | loop_var: device_role 89 | label: "{{ device_role['name'] }}" 90 | tags: [ devices ] 91 | 92 | - name: "TASK 7: SETUP VLANS" 93 | netbox.netbox.netbox_vlan: 94 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 95 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 96 | data: 97 | name: "VLAN{{ vlan.vid }}" 98 | vid: "{{ vlan.vid }}" 99 | site: "MINNESOTA01" 100 | description: "{{ vlan.desc }}" 101 | state: "{{ install_state }}" 102 | register: result 103 | loop: "{{ vlans }}" 104 | loop_control: 105 | loop_var: vlan 106 | label: "{{ vlan['vid'] }}" 107 | tags: [ ipam ] 108 | 109 | - name: "TASK 8: SETUP RFC1918 RIR" 110 | netbox.netbox.netbox_rir: 111 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 112 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 113 | data: "{{ rir }}" 114 | state: "{{ install_state }}" 115 | loop: "{{ rirs }}" 116 | loop_control: 117 | loop_var: rir 118 | label: "{{ rir['name'] }}" 119 | tags: [ ipam ] 120 | 121 | - name: "TASK 8: SETUP AGGREGRATES" 122 | netbox.netbox.netbox_aggregate: 123 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 124 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 125 | data: 126 | prefix: "{{ aggregate.name }}" 127 | description: "{{ aggregate.desc }}" 128 | rir: "{{ aggregate.rir }}" 129 | state: "{{ install_state }}" 130 | loop: "{{ aggregates }}" 131 | loop_control: 132 | loop_var: aggregate 133 | label: "{{ aggregate['name'] }}" 134 | tags: [ ipam ] 135 | 136 | - name: "TASK 9: SETUP PREFIXES" 137 | netbox.netbox.netbox_prefix: 138 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 139 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 140 | data: 141 | family: 4 142 | prefix: "{{ prefix.prefix }}" 143 | site: "{{ prefix.site | default(omit) }}" 144 | status: "{{ prefix.status | default('Active') }}" 145 | description: "{{ prefix.desc }}" 146 | is_pool: "{{ prefix.ispool }}" 147 | state: "{{ install_state }}" 148 | loop: "{{ prefixes }}" 149 | loop_control: 150 | loop_var: prefix 151 | label: "{{ prefix['prefix'] }}" 152 | tags: [ ipam ] 153 | 154 | - name: "TASK 10: SETUP CIRCUIT PROVIDER" 155 | netbox.netbox.netbox_provider: 156 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 157 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 158 | data: "{{ circuit_provider }}" 159 | state: "{{ install_state }}" 160 | loop: "{{ circuit_providers }}" 161 | loop_control: 162 | loop_var: circuit_provider 163 | label: "{{ circuit_provider['name'] }}" 164 | tags: [ circuit ] 165 | 166 | - name: Create type within Netbox with only required information 167 | netbox.netbox.netbox_circuit_type: 168 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 169 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 170 | data: "{{ circuit_type }}" 171 | state: "{{ install_state }}" 172 | loop: "{{ circuit_types }}" 173 | loop_control: 174 | loop_var: circuit_type 175 | label: "{{ circuit_type['name'] }}" 176 | tags: [ circuit ] 177 | 178 | - name: "CREATE LOCAL CIRCUIT" 179 | netbox.netbox.netbox_circuit: 180 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 181 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 182 | data: "{{ circuit }}" 183 | state: "{{ install_state }}" 184 | loop: "{{ circuits }}" 185 | loop_control: 186 | loop_var: circuit 187 | label: "{{ circuit['cid'] }}" 188 | tags: [ circuit ] 189 | -------------------------------------------------------------------------------- /01_setup_devices.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Playbook to setup devices from a static inventory list 3 | - name: "PLAY 1: SETUP vEOS" 4 | connection: network_cli 5 | hosts: eos 6 | tasks: 7 | - name: "TASK 1: EOS >> GATHER FACTS FROM DEVICE" 8 | eos_facts: 9 | gather_subset: "!config" 10 | 11 | - name: "TASK 2: NETBOX >> ADD DEVICE TO NETBOX" 12 | netbox.netbox.netbox_device: 13 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 14 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 15 | data: 16 | name: "{{ inventory_hostname }}" 17 | device_type: "{{ ansible_facts['net_model'] }}" 18 | platform: EOS 19 | serial: "{{ ansible_facts['net_serialnum'] }}" 20 | rack: MN_01 21 | position: "{{ rack_location }}" 22 | face: "Front" 23 | status: Active 24 | device_role: "Leaf" 25 | site: "MINNESOTA01" 26 | custom_fields: 27 | code_version: "{{ ansible_facts['net_version'] }}" 28 | 29 | - name: "TASK 3: NETBOX >> Add temporary interface" 30 | netbox.netbox.netbox_device_interface: 31 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 32 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 33 | data: 34 | device: "{{ inventory_hostname }}" 35 | name: Temporary_Interface 36 | form_factor: Virtual 37 | state: present 38 | 39 | - name: "TASK 4: NETBOX >> ADD IP ADDRESS OF ANSIBLE HOST" 40 | netbox.netbox.netbox_ip_address: 41 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 42 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 43 | data: 44 | family: 4 45 | address: "{{ ansible_host }}/24" 46 | status: active 47 | interface: 48 | name: Temporary_Interface 49 | device: "{{ inventory_hostname }}" 50 | 51 | - name: "TASK 5: NETBOX >> ADD DEVICE TO NETBOX" 52 | netbox.netbox.netbox_device: 53 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 54 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 55 | data: 56 | name: "{{ inventory_hostname }}" 57 | device_type: "{{ ansible_facts['net_model'] }}" 58 | platform: EOS 59 | serial: "{{ ansible_facts['net_serialnum'] }}" 60 | rack: MN_01 61 | face: "Front" 62 | status: Active 63 | site: "MINNESOTA01" 64 | primary_ip4: "{{ ansible_host }}/24" 65 | 66 | - name: "PLAY 2: ADD IOS" 67 | connection: network_cli 68 | hosts: routers, switches 69 | tasks: 70 | - name: "TASK 1: IOS >> GATHER FACTS FROM DEVICE" 71 | ios_facts: 72 | gather_subset: "!config" 73 | 74 | - name: "TASK 2: NETBOX >> ADD DEVICE TO NETBOX" 75 | netbox.netbox.netbox_device: 76 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 77 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 78 | data: 79 | name: "{{ inventory_hostname }}" 80 | device_type: "{{ ansible_facts['net_model'] }}" 81 | platform: IOS 82 | serial: "{{ ansible_facts['net_serialnum'] }}" 83 | rack: MN_01 84 | face: "Front" 85 | status: Active 86 | device_role: "{{ inventory_hostname | get_role_from_hostname }}" 87 | site: "MINNESOTA01" 88 | custom_fields: 89 | code_version: "{{ ansible_facts['net_version'] }}" 90 | 91 | - name: "TASK 3: Add temporary interface" 92 | netbox.netbox.netbox_device_interface: 93 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 94 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 95 | data: 96 | device: "{{ inventory_hostname }}" 97 | name: Temporary_Interface 98 | form_factor: Virtual 99 | state: present 100 | 101 | - name: "TASK 4: ADD IP ADDRESS OF ANSIBLE HOST" 102 | netbox.netbox.netbox_ip_address: 103 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 104 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 105 | data: 106 | family: 4 107 | address: "{{ ansible_host }}/24" 108 | status: active 109 | interface: 110 | name: Temporary_Interface 111 | device: "{{ inventory_hostname }}" 112 | 113 | - name: "TASK 5: ADD DEVICE TO NETBOX" 114 | netbox.netbox.netbox_device: 115 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 116 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 117 | data: 118 | name: "{{ inventory_hostname }}" 119 | device_type: "{{ ansible_facts['net_model'] }}" 120 | platform: IOS 121 | serial: "{{ ansible_facts['net_serialnum'] }}" 122 | rack: MN_01 123 | face: "Front" 124 | status: Active 125 | device_role: "{{ inventory_hostname | get_role_from_hostname }}" 126 | site: "MINNESOTA01" 127 | primary_ip4: "{{ ansible_host }}/24" 128 | 129 | - name: "PLAY 3: ADD NXOS" 130 | connection: network_cli 131 | hosts: nxos 132 | tags: [ nxos ] 133 | tasks: 134 | - name: "TASK 1: NXOS >> GATHER FACTS FROM DEVICE" 135 | nxos_facts: 136 | gather_subset: "!config" 137 | 138 | - name: "TASK 2: ADD DEVICE TO NETBOX" 139 | netbox.netbox.netbox_device: 140 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 141 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 142 | data: 143 | name: "{{ inventory_hostname }}" 144 | device_type: "{{ ansible_facts['net_model'] }}" 145 | platform: NXOS 146 | serial: "{{ ansible_facts['net_serialnum'] }}" 147 | rack: MN_01 148 | face: "Front" 149 | status: Active 150 | device_role: "{{ inventory_hostname | get_role_from_hostname }}" 151 | site: "MINNESOTA01" 152 | custom_fields: 153 | code_version: "{{ ansible_facts['net_version'] }}" 154 | 155 | - name: "TASK 3: Add temporary interface" 156 | netbox.netbox.netbox_device_interface: 157 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 158 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 159 | data: 160 | device: "{{ inventory_hostname }}" 161 | name: Temporary_Interface 162 | form_factor: Virtual 163 | state: present 164 | 165 | - name: "TASK 4: ADD IP ADDRESS OF ANSIBLE HOST" 166 | netbox.netbox.netbox_ip_address: 167 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 168 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 169 | data: 170 | family: 4 171 | address: "{{ ansible_host }}/24" 172 | status: active 173 | interface: 174 | name: Temporary_Interface 175 | device: "{{ inventory_hostname }}" 176 | 177 | - name: "TASK 5: ADD DEVICE TO NETBOX" 178 | netbox.netbox.netbox_device: 179 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 180 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 181 | data: 182 | name: "{{ inventory_hostname }}" 183 | device_type: "{{ ansible_facts['net_model'] }}" 184 | platform: NXOS 185 | serial: "{{ ansible_facts['net_serialnum'] }}" 186 | rack: MN_01 187 | face: "Front" 188 | status: Active 189 | device_role: "{{ inventory_hostname | get_role_from_hostname }}" 190 | site: "MINNESOTA01" 191 | primary_ip4: "{{ ansible_host }}/24" 192 | 193 | - name: "PLAY 4: Add BLANK NODE" 194 | connection: local 195 | hosts: localhost 196 | tags: [ setup_demo ] 197 | tasks: 198 | 199 | - name: "TASK 1: ADD DEVICE TO NETBOX" 200 | netbox.netbox.netbox_device: 201 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 202 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 203 | data: 204 | name: "sw-1:2" 205 | device_type: "IOSv" 206 | platform: IOS 207 | serial: "ANSIBLE_FTW" 208 | rack: MN_01 209 | status: Active 210 | device_role: "Switch" 211 | site: "MINNESOTA01" 212 | 213 | - name: "PLAY 5: SETUP CLUSTER" 214 | connection: local 215 | hosts: localhost 216 | vars: 217 | vms: 218 | - { name: gns3, address: 192.168.0.101/24 } 219 | - { name: awx, address: 192.168.0.102/24 } 220 | - { name: netbox03, address: 192.168.0.103/24 } 221 | - { name: prometheus, address: 192.168.0.104/24 } 222 | - { name: docker01, address: 192.168.0.105/24 } 223 | - { name: docker02, address: 192.168.0.106/24 } 224 | pves: 225 | - { name: pve01, address: "192.168.0.254/24" } 226 | - { name: pve02, address: "192.168.0.253/24" } 227 | netbox_headers: 228 | Content-Type: "application/json" 229 | Authorization: "token {{ lookup('ENV', 'NETBOX_API_KEY') }}" 230 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 231 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 232 | tasks: 233 | - name: "TASK 0: CREATE CLUSTER TYPE" 234 | netbox.netbox.netbox_cluster_type: 235 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 236 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 237 | data: 238 | name: Proxmox 239 | state: present 240 | 241 | - name: "TASK 1: SYS >> ADD VM CLUSTER" 242 | netbox.netbox.netbox_cluster: 243 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 244 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 245 | data: 246 | name: JoshV_Cluster1 247 | cluster_type: Proxmox 248 | state: present 249 | 250 | - name: "TASK 2: SYS >> CREATE CLUSTER HOSTS" 251 | netbox.netbox.netbox_device: 252 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 253 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 254 | data: 255 | name: "{{ item.name }}" 256 | device_type: CustomServer 257 | device_role: Server 258 | site: minnesota01 259 | loop: "{{ pves }}" 260 | 261 | - name: "TASK 3: SYS >> SETUP SOME VMs" 262 | netbox.netbox.netbox_virtual_machine: 263 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 264 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 265 | data: 266 | name: "{{ item.name }}" 267 | cluster: JoshV_Cluster1 268 | state: present 269 | loop: "{{ vms }}" 270 | 271 | - name: "TASK 4: SYS >> Create interfaces" 272 | netbox.netbox.netbox_vm_interface: 273 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 274 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 275 | data: 276 | virtual_machine: "{{ item.name }}" 277 | name: eth0 278 | state: present 279 | loop: "{{ vms }}" 280 | 281 | - name: "TASK 5: SYS >> ASSIGN IP TO VM" 282 | uri: 283 | url: "{{ lookup('ENV', 'NETBOX_URL') }}/api/ipam/ip-addresses/" 284 | method: POST 285 | headers: "{{ netbox_headers }}" 286 | status_code: 201 287 | body_format: json 288 | body: { 289 | "address": "{{ item.address }}", 290 | "status": 1, 291 | "interface": "{{ item.name | get_interface_id(netbox_headers) }}" 292 | } 293 | loop: "{{ vms }}" 294 | 295 | - name: "TASK 6: SYS >> ASSIGN PRIMARY ADDRESS TO THE DEVICE" 296 | netbox.netbox.netbox_virtual_machine: 297 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 298 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 299 | data: 300 | name: "{{ item.name }}" 301 | cluster: JoshV_Cluster1 302 | primary_ip4: "{{ item.address }}" 303 | state: present 304 | loop: "{{ vms }}" 305 | -------------------------------------------------------------------------------- /02_setup_interfaces.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "PLAY 1: ADD INTERFACES TO NETBOX AND CLEANUP TEMP" 3 | connection: network_cli 4 | hosts: platforms_nxos, platforms_ios, platforms_eos 5 | tags: [ ios, nxos ] 6 | tasks: 7 | - name: "TASK 1A: NXOS >> GET NXOS FACTS" 8 | nxos_facts: 9 | gather_subset: "!config" 10 | when: "ansible_network_os == 'nxos'" 11 | 12 | - name: "TASK 1B: IOS >> GET IOS FACTS" 13 | ios_facts: 14 | gather_subset: "!config" 15 | when: "ansible_network_os == 'ios'" 16 | 17 | - name: "TASK 1C: EOS >> GATHER FACTS FROM DEVICE" 18 | eos_facts: 19 | gather_subset: "!config" 20 | when: "ansible_network_os == 'eos'" 21 | 22 | - name: "TASK 2: NETBOX >> ADD INTERFACES TO NETBOX" 23 | netbox.netbox.netbox_device_interface: 24 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 25 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 26 | data: 27 | device: "{{ inventory_hostname }}" 28 | name: "{{ item.key }}" 29 | form_factor: "{{ item.key | get_interface_type }}" 30 | mac_address: "{{ item.value.macaddress | convert_mac_address }}" 31 | state: present 32 | with_dict: 33 | - "{{ ansible_facts['net_interfaces'] }}" 34 | loop_control: 35 | label: "{{ item.key }}" 36 | 37 | - name: "TASK 3: NETBOX >> ADD IP ADDRESSES TO PROPER INTERFACE" 38 | netbox.netbox.netbox_ip_address: 39 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 40 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 41 | data: 42 | family: 4 43 | address: "{{ item['address'] }}" 44 | status: active 45 | interface: 46 | name: "{{ item['interface'] }}" 47 | device: "{{ inventory_hostname }}" 48 | with_items: 49 | - "{{ ansible_facts | build_ipv4_from_facts }}" 50 | 51 | - name: "TASK 4: NETBOX >> REMOVE TEMPORARY INTERFACE" 52 | netbox.netbox.netbox_device_interface: 53 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 54 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 55 | data: 56 | device: "{{ inventory_hostname }}" 57 | name: Temporary_Interface 58 | form_factor: Virtual 59 | state: absent 60 | 61 | - name: "TASK 5: NETBOX >> SET PRIMARY IP" 62 | netbox.netbox.netbox_device: 63 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 64 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 65 | data: 66 | name: "{{ inventory_hostname }}" 67 | device_type: "{{ ansible_facts['net_model'] }}" 68 | serial: "{{ ansible_facts['net_serialnum'] }}" 69 | rack: MN_01 70 | device_role: "{{ inventory_hostname | get_role_from_hostname }}" 71 | primary_ip4: "{{ item['address'] }}" 72 | with_items: 73 | - "{{ ansible_facts | build_ipv4_from_facts }}" 74 | when: "ansible_host == item['address'].split('/')[0]" 75 | -------------------------------------------------------------------------------- /03_audit_netbox.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Playbook to read information from device and netbox and compare 3 | - name: "PLAY 1: READ NETWORK DEVICE INFORMATION AND COMPARE NETBOX" 4 | hosts: platforms_ios, platforms_eos, platforms_nxos 5 | become: yes 6 | become_method: enable 7 | connection: network_cli 8 | vars: 9 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 10 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 11 | netbox_headers: 12 | Content-Type: "application/json" 13 | Authorization: "token {{ netbox_token }}" 14 | netbox_serial_list: [] 15 | device_serial_list: [] 16 | tasks: 17 | - name: "TASK 1A: NXOS >> GET NXOS FACTS" 18 | nxos_facts: 19 | gather_subset: "!config" 20 | when: "ansible_network_os == 'nxos'" 21 | 22 | - name: "TASK 1B: IOS >> GET IOS FACTS" 23 | ios_facts: 24 | gather_subset: "!config" 25 | when: "ansible_network_os == 'ios'" 26 | 27 | - name: "TASK 1C: EOS >> GATHER FACTS FROM DEVICE" 28 | eos_facts: 29 | gather_subset: "!config" 30 | when: "ansible_network_os == 'eos'" 31 | 32 | - name: "TASK 2: NETBOX >> GATHER NETBOX DATA" 33 | uri: 34 | url: "{{ netbox_url }}/api/dcim/devices/?q={{ inventory_hostname }}" 35 | headers: "{{ netbox_headers }}" 36 | method: GET 37 | status_code: 200 38 | register: netbox_device_results 39 | 40 | - name: "TASK 3: SYS >> BUILD NETBOX SERIAL NUMBER LIST FROM NETBOX RESPONSES" 41 | set_fact: 42 | netbox_serial_list: "{{ netbox_serial_list + [ item.serial ] }}" 43 | loop: "{{ netbox_device_results['json']['results'] }}" 44 | loop_control: 45 | label: "{{ item.name }}" 46 | 47 | - name: "TASK 4: SYS >> BUILD IOS DEVICE SERIAL LIST USING FILTER" 48 | set_fact: 49 | device_serial_list: "{{ ansible_facts | get_ciscoios_serial_list }}" 50 | 51 | - name: "TASK 5: SYS >> VERIFY DEVICE DATA VS NETBOX - CUSTOM FILTER" 52 | assert: 53 | that: 54 | - "device_serial_list | compare_lists(netbox_serial_list)" 55 | fail_msg: 56 | - "Device Serial List: {{ device_serial_list }}" 57 | - "NetBox Serial List: {{ netbox_serial_list }}" 58 | success_msg: 59 | - "Device Serial List: {{ device_serial_list }}" 60 | - "NetBox Serial List: {{ netbox_serial_list }}" 61 | ... 62 | -------------------------------------------------------------------------------- /04_setup_vm_clusters.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "PLAY 1: SETUP CLUSTER" 3 | connection: local 4 | hosts: localhost 5 | vars: 6 | vms: 7 | - { name: gns3, address: 192.168.0.101/24 } 8 | - { name: awx, address: 192.168.0.102/24 } 9 | - { name: netbox03, address: 192.168.0.103/24 } 10 | - { name: prometheus, address: 192.168.0.104/24 } 11 | - { name: docker01, address: 192.168.0.105/24 } 12 | - { name: docker02, address: 192.168.0.106/24 } 13 | pves: 14 | - { name: pve01, address: "192.168.0.254/24" } 15 | - { name: pve02, address: "192.168.0.253/24" } 16 | netbox_headers: 17 | Content-Type: "application/json" 18 | Authorization: "token {{ lookup('ENV', 'NETBOX_API_KEY') }}" 19 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 20 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 21 | tasks: 22 | - name: "TASK 0: CREATE CLUSTER TYPE" 23 | netbox.netbox.netbox_cluster_type: 24 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 25 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 26 | data: 27 | name: Proxmox 28 | state: present 29 | 30 | - name: "TASK 1: SYS >> ADD VM CLUSTER" 31 | netbox.netbox.netbox_cluster: 32 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 33 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 34 | data: 35 | name: JoshV_Cluster1 36 | cluster_type: Proxmox 37 | state: present 38 | 39 | - name: "TASK 2: SYS >> CREATE CLUSTER HOSTS" 40 | netbox.netbox.netbox_device: 41 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 42 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 43 | data: 44 | name: "{{ item.name }}" 45 | device_type: CustomServer 46 | device_role: Server 47 | site: minnesota01 48 | loop: "{{ pves }}" 49 | 50 | - name: "TASK 3: SYS >> SETUP SOME VMs" 51 | netbox.netbox.netbox_virtual_machine: 52 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 53 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 54 | data: 55 | name: "{{ item.name }}" 56 | cluster: JoshV_Cluster1 57 | state: present 58 | loop: "{{ vms }}" 59 | 60 | - name: "TASK 4: SYS >> Create interfaces" 61 | netbox.netbox.netbox_vm_interface: 62 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 63 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 64 | data: 65 | virtual_machine: "{{ item.name }}" 66 | name: eth0 67 | state: present 68 | loop: "{{ vms }}" 69 | 70 | - name: "TASK 5: SYS >> ASSIGN IP TO VM" 71 | uri: 72 | url: "{{ lookup('ENV', 'NETBOX_URL') }}/api/ipam/ip-addresses/" 73 | method: POST 74 | headers: "{{ netbox_headers }}" 75 | status_code: 201 76 | body_format: json 77 | body: { 78 | "address": "{{ item.address }}", 79 | "status": 1, 80 | "interface": "{{ item.name | get_interface_id(netbox_headers) }}" 81 | } 82 | loop: "{{ vms }}" 83 | 84 | - name: "TASK 6: SYS >> ASSIGN PRIMARY ADDRESS TO THE DEVICE" 85 | netbox.netbox.netbox_virtual_machine: 86 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 87 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 88 | data: 89 | name: "{{ item.name }}" 90 | cluster: JoshV_Cluster1 91 | primary_ip4: "{{ item.address }}" 92 | state: present 93 | loop: "{{ vms }}" 94 | -------------------------------------------------------------------------------- /2020-04-Ansible_Minneapolis_Meetup.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jvanderaa/ansible_netbox_demo/272f2860325b9e2333e09610f0fca12591e6064c/2020-04-Ansible_Minneapolis_Meetup.pdf -------------------------------------------------------------------------------- /99_clean_env.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "PLAY 1: CLEANUP NETWORK DEVICES" 3 | connection: local 4 | hosts: platforms_nxos, platforms_ios, platforms_eos 5 | tags: [ ios, nxos ] 6 | tasks: 7 | - name: "TASK 1: NETBOX >> REMOVE DEVICE TO NETBOX" 8 | netbox.netbox.netbox_device: 9 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 10 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 11 | data: 12 | name: "{{ inventory_hostname }}" 13 | state: absent 14 | 15 | - name: "TASK 2: DELETE SW1:2 DEVICE TO NETBOX" 16 | netbox.netbox.netbox_device: 17 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 18 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 19 | data: 20 | name: "sw-1:2" 21 | device_type: "IOSv" 22 | platform: IOS 23 | serial: "ANSIBLE_FTW" 24 | rack: MN_01 25 | status: Active 26 | device_role: "Switch" 27 | site: "MINNESOTA01" 28 | state: absent 29 | delegate_to: localhost 30 | run_once: yes 31 | 32 | - name: "PLAY 2: DELETE VM INFO" 33 | connection: local 34 | hosts: localhost 35 | vars: 36 | vms: 37 | - { name: gns3, address: 192.168.0.101/24 } 38 | - { name: awx, address: 192.168.0.102/24 } 39 | - { name: netbox03, address: 192.168.0.103/24 } 40 | - { name: prometheus, address: 192.168.0.104/24 } 41 | - { name: docker01, address: 192.168.0.105/24 } 42 | - { name: docker02, address: 192.168.0.106/24 } 43 | pves: 44 | - { name: pve01, address: "192.168.0.254/24" } 45 | - { name: pve02, address: "192.168.0.253/24" } 46 | netbox_headers: 47 | Content-Type: "application/json" 48 | Authorization: "token {{ lookup('ENV', 'NETBOX_API_KEY') }}" 49 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 50 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 51 | tasks: 52 | - name: "TASK 1: SYS >> DELETE SOME VMs" 53 | netbox.netbox.netbox_virtual_machine: 54 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 55 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 56 | data: 57 | name: "{{ item.name }}" 58 | cluster: JoshV_Cluster1 59 | state: absent 60 | loop: "{{ vms }}" 61 | 62 | - name: "TASK 2: SYS >> DELETE CLUSTER HOSTS" 63 | netbox.netbox.netbox_device: 64 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 65 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 66 | data: 67 | name: "{{ item.name }}" 68 | device_type: CustomServer 69 | device_role: Server 70 | site: minnesota01 71 | state: absent 72 | loop: "{{ pves }}" 73 | 74 | - name: "TASK 3: SYS >> DELETE VM CLUSTER" 75 | netbox.netbox.netbox_cluster: 76 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 77 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 78 | data: 79 | name: JoshV_Cluster1 80 | cluster_type: Proxmox 81 | state: absent 82 | 83 | - name: "TASK 4: DELETE CLUSTER TYPE" 84 | netbox.netbox.netbox_cluster_type: 85 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 86 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 87 | data: 88 | name: Proxmox 89 | state: absent 90 | 91 | - name: "PLAY 3: CLEANUP BASE NETBOX" 92 | hosts: localhost 93 | connection: local 94 | vars: 95 | install_state: absent 96 | tasks: 97 | - name: "TASK 1: DELETE LOCAL CIRCUIT" 98 | netbox.netbox.netbox_circuit: 99 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 100 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 101 | data: 102 | cid: my-circuit-id 103 | state: "{{ install_state }}" 104 | 105 | - name: "TASK 2: DELETE CIRCUIT TYPE" 106 | netbox.netbox.netbox_circuit_type: 107 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 108 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 109 | data: 110 | name: Cable 111 | state: "{{ install_state }}" 112 | tags: [ circuit ] 113 | 114 | - name: "TASK 3: DELETE CIRCUIT PROVIDER" 115 | netbox.netbox.netbox_provider: 116 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 117 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 118 | data: 119 | name: Charter Spectrum 120 | asn: 7843 121 | account: AGOODPAYING_ONE 122 | portal_url: http://spectrum.net 123 | noc_contact: noc@spectrum.net 124 | comments: "BEST I CAN GET" 125 | state: "{{ install_state }}" 126 | tags: [ circuit ] 127 | 128 | - name: "TASK 4: DELETE PREFIXES" 129 | netbox.netbox.netbox_prefix: 130 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 131 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 132 | data: 133 | family: 4 134 | prefix: "{{ item.prefix }}" 135 | site: "MINNESOTA01" 136 | status: Active 137 | description: "{{ item.desc }}" 138 | is_pool: "{{ item.ispool }}" 139 | state: "{{ install_state }}" 140 | loop: 141 | - { prefix: 10.50.50.0/24, desc: GNS3 Loopbacks, ispool: true } 142 | - { prefix: 172.31.0.0/24, desc: GNS3 - DC, ispool: false } 143 | - { prefix: 172.31.1.0/24, desc: GNS3 - DC Mgmt, ispool: false } 144 | - { prefix: 192.168.0.0/24, desc: GNS3 Remote, ispool: false } 145 | - { prefix: 192.0.2.0/24, desc: Example Network, ispool: true } 146 | tags: [ ipam ] 147 | 148 | - name: "TASK 5: DELETE AGGREGRATES" 149 | netbox.netbox.netbox_aggregate: 150 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 151 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 152 | data: 153 | prefix: "{{ item.name }}" 154 | description: "{{ item.desc }}" 155 | rir: RFC1918 156 | state: "{{ install_state }}" 157 | loop: 158 | - { name: "10.0.0.0/8", desc: RFC1918 - 10 } 159 | - { name: "172.16.0.0/12", desc: RFC1918 - 172 } 160 | - { name: "192.0.2.0/24", desc: RFC5735 - 192.0.2.0 } 161 | - { name: "192.168.0.0/16", desc: RFC1918 - 192 } 162 | tags: [ ipam ] 163 | 164 | - name: "TASK 6: DELETE RFC1918 RIR" 165 | netbox.netbox.netbox_rir: 166 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 167 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 168 | data: 169 | name: RFC1918 170 | is_private: True 171 | state: "{{ install_state }}" 172 | tags: [ ipam ] 173 | 174 | - name: "TASK 7: DELETE VLANS" 175 | netbox.netbox.netbox_vlan: 176 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 177 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 178 | data: 179 | name: "VLAN{{ item.vid }}" 180 | vid: "{{ item.vid }}" 181 | site: "MINNESOTA01" 182 | description: "{{ item.desc }}" 183 | state: "{{ install_state }}" 184 | register: result 185 | tags: [ ipam ] 186 | loop: 187 | - { vid: 100, desc: Demo VLAN } 188 | - { vid: 200, desc: Primary VLAN } 189 | - { vid: 300, desc: Secondary VLAN } 190 | 191 | - name: "TASK 8: DELETE DEVICE ROLES" 192 | netbox.netbox.netbox_device_role: 193 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 194 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 195 | data: 196 | name: "{{ item.name }}" 197 | color: "{{ item.color }}" 198 | vm_role: "{{ item.vmrole }}" 199 | state: "{{ install_state }}" 200 | loop: 201 | - { name: Firewall, color: FF0000, vmrole: true } 202 | - { name: Router, color: 000080, vmrole: true } 203 | - { name: Switch, color: 008000, vmrole: true } 204 | - { name: Server, color: "000000", vmrole: false } 205 | - { name: VM, color: 00FFFF, vmrole: true } 206 | - { name: Leaf, color: 008000, vmrole: false } 207 | - { name: Spine, color: 0000FF, vmrole: false } 208 | tags: [ devices ] 209 | 210 | - name: "TASK 9: DELETE PLATFORMS" 211 | netbox.netbox.netbox_platform: 212 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 213 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 214 | data: 215 | name: "{{ item.name }}" 216 | slug: "{{ item.slug }}" 217 | state: "{{ install_state }}" 218 | loop: 219 | - { name: EOS, slug: eos } 220 | - { name: IOS, slug: ios } 221 | - { name: IOS-XE, slug: ios-xe } 222 | - { name: NXOS, slug: nxos } 223 | - { name: Raspbian, slug: raspbian } 224 | - { name: Ubuntu 18.04, slug: ubuntu1804 } 225 | - { name: Ubuntu 16.04, slug: ubuntu1604 } 226 | - { name: Ubuntu 20.04, slug: ubuntu2004 } 227 | - { name: Windows 2012, slug: win2012 } 228 | - { name: Windows 2016, slug: win2016 } 229 | - { name: Windows 2019, slug: win2019 } 230 | tags: [ devices ] 231 | 232 | - name: "TASK 10: DELETE DEVICE TYPES" 233 | netbox.netbox.netbox_device_type: 234 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 235 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 236 | data: 237 | model: "{{ item.model }}" 238 | manufacturer: "{{ item.manufacturer }}" 239 | slug: "{{ item.slug }}" 240 | part_number: "{{ item.part_number }}" 241 | u_height: 1 242 | is_full_depth: "{{ item.full_depth }}" 243 | state: "{{ install_state }}" 244 | loop: 245 | - {"model": "IOSv", "manufacturer": "Cisco", "slug": "iosv", "part_number": "iosv", "full_depth": False} 246 | - {"model": "nxosv", "manufacturer": "Cisco", "slug": "nxosv", "part_number": "nxosv", "full_depth": False} 247 | - {"model": "nx-osv-chassis-nx-osv-supervisor-module", "manufacturer": "Cisco", "slug": "nx-osv-chassis-nx-osv-supervisor-module", "part_number": "nx-osv-chassis-nx-osv-supervisor-module", "full_depth": False} 248 | - {"model": "CSR1000v", "manufacturer": "Cisco", "slug": "csr1000v", "part_number": "csr1000v", "full_depth": False} 249 | - {"model": "ASAv", "manufacturer": "Cisco", "slug": "asav", "part_number": "asav", "full_depth": False} 250 | - {"model": "CustomServer", "manufacturer": "CustomBuild", "slug": "customserver", "part_number": "home", "full_depth": True} 251 | - {"model": "vEOS", "manufacturer": "Arista", "slug": "veos", "part_number": "veos", "full_depth": False} 252 | tags: [ devices ] 253 | 254 | - name: "TASK 11: DELETE MANUFACTURERS" 255 | netbox.netbox.netbox_manufacturer: 256 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 257 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 258 | data: 259 | name: "{{ item }}" 260 | state: "{{ install_state }}" 261 | loop: 262 | - Arista 263 | - Cisco 264 | - CustomBuild 265 | - Raspberry Pi 266 | tags: [ devices ] 267 | 268 | - name: "TASK 12: DELETE RACKS" 269 | netbox.netbox.netbox_rack: 270 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 271 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 272 | data: 273 | name: "MN_01" 274 | site: "MINNESOTA01" 275 | state: "{{ install_state }}" 276 | tags: [ sites, devices ] 277 | 278 | - name: "TASK 13: DELETE SITES" 279 | netbox.netbox.netbox_site: 280 | netbox_url: "{{ lookup('ENV', 'NETBOX_URL') }}" 281 | netbox_token: "{{ lookup('ENV', 'NETBOX_API_KEY') }}" 282 | data: 283 | name: "MINNESOTA01" 284 | time_zone: America/Chicago 285 | status: active 286 | description: Josh's Home 287 | contact_name: Josh V 288 | contact_email: josh@josh-v.com 289 | state: "{{ install_state }}" 290 | register: site_setup 291 | tags: [ sites, devices ] 292 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7.7-slim-buster 2 | 3 | COPY . /local 4 | WORKDIR /local 5 | RUN apt-get update && apt-get install apt-utils -y 6 | RUN cat requirements.system | xargs apt-get install -y 7 | RUN mkdir /opt/ntc-templates && \ 8 | git clone https://github.com/networktocode/ntc-templates.git /opt/ntc-templates 9 | 10 | RUN pip install --no-cache -r requirements.txt 11 | RUN ansible-galaxy collection install -r requirements.yml 12 | CMD /bin/bash -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | IMG_NAME=ntc-jv/ansible 2 | IMG_VERSION=0.2.0 3 | 4 | .DEFAULT_GOAL := test 5 | .PHONY: test 6 | test: lint unit 7 | 8 | .PHONY: build 9 | build: 10 | docker build -t $(IMG_NAME):$(IMG_VERSION) . 11 | 12 | .PHONY: cli 13 | cli: 14 | docker run -it --env-file=.env \ 15 | -v $(shell pwd):/local \ 16 | -w /local \ 17 | $(IMG_NAME):$(IMG_VERSION) bash 18 | 19 | .PHONY: pylint 20 | pylint: 21 | find ./ -name "*.py" | xargs pylint 22 | 23 | .PHONY: lint 24 | lint: 25 | @echo "Starting lint" 26 | docker run -v $(shell pwd):/local $(IMG_NAME):$(IMG_VERSION) yamllint --strict . 27 | docker run -v $(shell pwd):/local $(IMG_NAME):$(IMG_VERSION) ansible-lint . 28 | docker run -v $(shell pwd):/local $(IMG_NAME):$(IMG_VERSION) make pylint 29 | docker run -v $(shell pwd):/local $(IMG_NAME):$(IMG_VERSION) black --check 30 | docker run -v $(shell pwd):/local $(IMG_NAME):$(IMG_VERSION) bandit --recursive --config .bandit.yml ./ 31 | 32 | @echo "Completed lint" 33 | 34 | .PHONY: unit 35 | unit: 36 | @echo "Starting unit tests" 37 | docker run -v $(shell pwd):/local $(IMG_NAME):$(IMG_VERSION) pytest -vv 38 | docker run -v $(shell pwd):/local $(IMG_NAME):$(IMG_VERSION) ansible-playbook tests/pb.unittest.yml 39 | @echo "Completed unit tests" 40 | 41 | .PHONY: unit-pytest 42 | unit-pytest: 43 | @echo "Starting unit tests" 44 | docker run -v $(shell pwd):/local $(IMG_NAME):$(IMG_VERSION) pytest -vv 45 | @echo "Completed unit tests" 46 | 47 | .PHONY: unit-ansible 48 | unit-ansible: 49 | @echo "Starting unit tests" 50 | docker run -v $(shell pwd):/local $(IMG_NAME):$(IMG_VERSION) ansible-playbook tests/pb.unittest.yml 51 | @echo "Completed unit tests" 52 | 53 | .PHONY: demo_show_setup 54 | demo_show_setup: 55 | @echo "Starting the Setup demo" 56 | docker run -e "TERM=xterm-256color" -v $(shell pwd):/local $(IMG_NAME):$(IMG_VERSION) ansible-playbook setup_netbox.yml --limit rtr-1 57 | 58 | .PHONY: demo_setup 59 | demo_setup: 60 | @echo "Starting the Setup demo" 61 | ansible-playbook setup_netbox.yml --tags ios 62 | 63 | .PHONY: demo_audit 64 | demo_audit: 65 | @echo "Starting Audit of single device" 66 | ansible-playbook audit_netbox.yml --limit wanrtr01 67 | read 68 | ansible-playbook audit_netbox.yml 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ansible NetBox Demo 2 | 3 | ![](https://github.com/jvanderaa/ansible_netbox_demo/workflows/CI/badge.svg) 4 | 5 | ## Newer Library Notice 6 | 7 | Most of my day to day effort and work is with [Nautobot](https://nautobot.readthedocs.io). I find this more friendly for the immediate goals in front of me. That said, please take a look at the Nautobot version of this repo: https://github.com/jvanderaa/nautobot_ansible_import 8 | 9 | Feel free to take a look at the Nautobot open source project itself - [https://nautobot.readthedocs.io](https://nautobot.readthedocs.io). 10 | ## Archival Notice 11 | 12 | This project is not actively being maintained. If there is an issue and there is time available, I will work on it. Alternatively if you wish to submit an issue and PR to the repo, I welcome it. 13 | 14 | ## Nautobot 15 | 16 | Nautobot is a fork from NetBox 2.10 to take the capabilities even further in the network automation space. Take a look at the info on the [NTC home page](https://www.networktocode.com/nautobot/). The same features that have been part of NetBox are still around and will evolve. If you want to take a test drive of Nautobot, take a look at [https://demo.nautobot.com](https://demo.nautobot.com). 17 | 18 | ## Link to recording 19 | 20 | Thanks to the Red Hat team that had the recording done of this. You can find it on YouTube at 21 | [https://www.youtube.com/watch?v=GyQf5F0gr3w](https://www.youtube.com/watch?v=GyQf5F0gr3w). 22 | 23 | ## Most Recent Updates 24 | 25 | I have moved all of the data that was previously configured to be added to NetBox within the 00_setup_netbox_devices.yml 26 | file to be configured in the all.yml. If looking to bootstrap your NetBox environment, this is the 27 | main information that will need to get updated. 28 | 29 | **Important Note** 30 | > This is **NOT** meant to be run constantly in your environment. Please do not use as such. This is 31 | > a bootstrapping setup, to get you running. The intent of NetBox is to become the Source of Truth 32 | > for your environment. If all you are doing is constantly updating NetBox from your devices, then 33 | > the source of truth is the network device, not NetBox. Please be working to get your environment 34 | > to the state that you have a Source of Truth that is NOT your device. 35 | 36 | ## Start 37 | 38 | To setup NetBox, checkout https://netbox.readthedocs.io 39 | Setup an API Key in the upper right admin panel 40 | Generate an environment of devices to use 41 | 42 | ## Requirements 43 | 44 | 1. NetBox Installation 45 | 2. Docker Desktop on system 46 | 3. If running on Windows, you do not have `make`. The Makefile is just shortcuts to be used. Take a 47 | look at the Makefile for the exact commands that replace the specific make commands. 48 | 49 | ## How to 50 | 51 | 1. Setup NetBox (see the beginning) 52 | 2. Setup a new file named .env that contains information about your NetBox environment for the container. See below for example config 53 | 3. Update your `group_vars/all.yml`, `group_vars/` and `host_vars/` accordingly with credentials to connect to the devices, see below for example config 54 | 4. Create a container - `make build` 55 | 5. Enter the container - `make cli` 56 | 6. Setup NetBox with executing `ansible-playbook 00_setup_netbox_devices.yml` 57 | 7. Setup Devices Into Netbox `ansible-playbook 01_setup_devices.yml -i start_inventory` replace `start_inventory` with whatever your static inventory file is 58 | 8. Setup Interfaces using dynamic inventory `ansible-playbook 02_setup_interfaces.yml -i netbox_inventory.yml` 59 | 9. Run the audit `ansible-playbook 03_audit_netbox.yml -i netbox_inventory.yml` where you have updated the information in the netbox_inventory.yml file 60 | 61 | 62 | ### ENV File setup 63 | 64 | ``` 65 | NETBOX_API_KEY= 66 | NETBOX_URL= 67 | ``` 68 | 69 | ### Group Vars, Host Vars 70 | 71 | ```yaml 72 | --- 73 | ansible_user: 74 | ansible_password: 75 | ansible_network_os: 76 | ``` 77 | 78 | hostvars will only contain information overriding data points. In the example, the only thing set in the host vars 79 | section is: 80 | 81 | ```yaml 82 | --- 83 | rack_location: 41 84 | ``` 85 | 86 | ## URLs 87 | 88 | Helpful [URLs](./urls.md) have a few other links that may be helpful. 89 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | action_warnings = False 3 | command_warnings = False 4 | ansible_force_color = True 5 | display_skipped_hosts = False 6 | filter_plugins = plugins/filter/ 7 | gathering = explicit 8 | host_key_checking = False 9 | library = /etc/ansible/library/ 10 | localhost_warning = False 11 | retry_files_enabled = False 12 | stdout_callback = yaml 13 | -------------------------------------------------------------------------------- /group_vars/all.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ntp_servers: 3 | - 162.159.200.1 4 | - 162.159.200.123 5 | dns_servers: 6 | - 1.1.1.3 7 | - 9.9.9.9 8 | sites: 9 | - name: "MINNESOTA01" 10 | time_zone: America/Chicago 11 | status: active 12 | description: Dev 13 | contact_name: Josh 14 | contact_email: josh@josh-v.com 15 | racks: 16 | - name: "MN_01" 17 | site: "MINNESOTA01" 18 | manufacturers: 19 | - Arista 20 | - Cisco 21 | - CustomBuild 22 | - Raspberry Pi 23 | device_types: 24 | - {"model": "ASAv", "manufacturer": "Cisco", "slug": "asav", "part_number": "asav", "full_depth": False} 25 | - {"model": "CSR1000v", "manufacturer": "Cisco", "slug": "csr1000v", "part_number": "csr1000v", "full_depth": False} 26 | - {"model": "CustomServer", "manufacturer": "CustomBuild", "slug": "customserver", "part_number": "home", "full_depth": True} 27 | - {"model": "IOSv", "manufacturer": "Cisco", "slug": "iosv", "part_number": "iosv", "full_depth": False} 28 | - {"model": "nx-osv-chassis-nx-osv-supervisor-module", "manufacturer": "Cisco", "slug": "nx-osv-chassis-nx-osv-supervisor-module", "part_number": "nx-osv-chassis-nx-osv-supervisor-module", "full_depth": False} 29 | - {"model": "nxosv", "manufacturer": "Cisco", "slug": "nxosv", "part_number": "nxosv", "full_depth": False} 30 | - {"model": "vEOS", "manufacturer": "Arista", "slug": "veos", "part_number": "veos", "full_depth": False} 31 | platforms: 32 | - { name: EOS, slug: eos } 33 | - { name: IOS, slug: ios } 34 | - { name: IOS-XE, slug: ios-xe } 35 | - { name: NXOS, slug: nxos } 36 | - { name: Raspbian, slug: raspbian } 37 | - { name: Ubuntu 18.04, slug: ubuntu1804 } 38 | - { name: Ubuntu 16.04, slug: ubuntu1604 } 39 | - { name: Ubuntu 20.04, slug: ubuntu2004 } 40 | - { name: Windows 2012, slug: win2012 } 41 | - { name: Windows 2016, slug: win2016 } 42 | - { name: Windows 2019, slug: win2019 } 43 | device_roles: 44 | - { name: Firewall, color: FF0000, vmrole: true } 45 | - { name: Leaf, color: 008000, vmrole: false } 46 | - { name: Router, color: 000080, vmrole: true } 47 | - { name: Server, color: "000000", vmrole: false } 48 | - { name: Spine, color: 0000FF, vmrole: false } 49 | - { name: Switch, color: 008000, vmrole: true } 50 | - { name: VM, color: 00FFFF, vmrole: true } 51 | vlans: 52 | - { vid: 100, desc: Demo VLAN } 53 | - { vid: 200, desc: Primary VLAN } 54 | - { vid: 300, desc: Secondary VLAN } 55 | rirs: 56 | - name: RFC1918 57 | is_private: True 58 | aggregates: 59 | - { name: "10.0.0.0/8", desc: RFC1918 - 10, rir: RFC1918 } 60 | - { name: "172.16.0.0/12", desc: RFC1918 - 172, rir: RFC1918 } 61 | - { name: "192.0.2.0/24", desc: RFC5735 - 192.0.2.0, rir: RFC1918 } 62 | - { name: "192.168.0.0/16", desc: RFC1918 - 192, rir: RFC1918 } 63 | prefixes: 64 | - { prefix: 10.50.50.0/24, desc: GNS3 Loopbacks, ispool: true } 65 | - { prefix: 172.31.0.0/24, desc: GNS3 - DC, ispool: false } 66 | - { prefix: 172.31.1.0/24, desc: GNS3 - DC Mgmt, ispool: false } 67 | - { prefix: 192.168.0.0/24, desc: GNS3 Remote, ispool: false } 68 | - { prefix: 192.0.2.0/24, desc: Example Network, ispool: true } 69 | circuit_providers: 70 | - name: Charter Spectrum 71 | asn: 7843 72 | account: in_good_standing 73 | portal_url: http://spectrum.net 74 | noc_contact: noc@spectrum.net 75 | comments: "Cable Provider" 76 | circuit_types: 77 | - name: Cable 78 | circuits: 79 | - cid: my-circuit-id 80 | provider: Charter Spectrum 81 | circuit_type: Cable 82 | status: Active 83 | install_date: "2016-06-01" 84 | commit_rate: 200000000 85 | description: Charter Spectrum Cable Internet 86 | comments: "Delivered" 87 | -------------------------------------------------------------------------------- /group_vars/eos.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 64646634663537613762373237373861376131393939663831326163383363643830616236393764 3 | 6139653562386235623136383963383533613566653062620a373466616535333634366337613366 4 | 30313464643739386631366130636234396466663662633436356664633935366666363433616536 5 | 3337636333633931630a376137366232366138643535363332356162336563333739613233393265 6 | 34313562623435643039366464316237613466656236393937613133306562633539383362383833 7 | 35376134396239333563633965663638393562373730666233363164396264343062336565633866 8 | 31623237666262623931353965613537303439663866363930306561343261373234326630373462 9 | 33353765656561626535 10 | -------------------------------------------------------------------------------- /group_vars/nxos.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 32613363656636356331666464316238353536623465633966353466616461346631383032396338 3 | 3661666136643862643330393637313935653165326663620a613332316663313266323730353262 4 | 63663137653066316161313636636335373166326535303263333761343534373430386434626533 5 | 3634663962353736320a316466396465333930336331356361323063383934326536633031373439 6 | 30653364346438623361613663383065303663386431303439363734663537326166383839373861 7 | 33663962626162313831633638643962656637393831396537336639343661386334306362343934 8 | 35626535386132323232636335393438376533343765616433303164313637346665613664306564 9 | 61626339643562383866 10 | -------------------------------------------------------------------------------- /group_vars/platforms_edgeos.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 39313139343034383063643861623965656236643965393331663238636665623664646432386166 3 | 3464386339656230663065373963666137626631646632660a313238333164646261303062353131 4 | 63393534666433666238303932343238646530656636393263633961313132656238303330366235 5 | 6439636436333031340a366437633361626531616662363163613963386166376234643665383465 6 | 61633634306263636532633838643837626137616431663536386130346661393864396435353761 7 | 37316231373432383835616633393634653265313730653661346563303736306664623036613333 8 | 65383266336561333238653966313335313530613061336562356231346262356535333261316632 9 | 34393163333730363931396434303335323838643531616437356536346237383337323761333437 10 | 6665 11 | -------------------------------------------------------------------------------- /group_vars/platforms_eos.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 35373536383563643164336539346630646138343337306334323237613637376338633937356438 3 | 3766356630396439333963333031633362346436616234640a663561323863353237306264623538 4 | 36393339323965383730316334343231353831383166336630366535373263653138303838643063 5 | 3966643164326335310a363264306430383138353963376163393462396163356631333530353765 6 | 65343966306334646231303866373361646538353565663733626563373734353062646665306630 7 | 62326636623331643738323763643730653131326430313862333032363437366435633039323338 8 | 336361643432646266363336353536343938 9 | -------------------------------------------------------------------------------- /group_vars/platforms_ios.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 39366464333839313763313139333833343338356261316665393133383532363930656463643163 3 | 6464393965366163626563303464663138363335373164380a303838346432613631313364386138 4 | 39316330323865643436356432336264323838626430326634656130633038343433316366346164 5 | 6466623936613564390a303661666238656433353538306165303265396164383733313166313264 6 | 34333361623533613332666132626231633361653538623239643436646531353130613033613964 7 | 3131343732396534663863626162346635633534633662366663 8 | -------------------------------------------------------------------------------- /group_vars/platforms_nxos.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 38663735313762643263333363336438336666656165643062373731653864383363343961623164 3 | 6364633830623530643733373062396339353661643435640a363131643730366363383031643839 4 | 33613531636336663337386630373734343535376538646330666162663032643336393936643032 5 | 3231376661313737610a633631373639613138346164393937383937613837353337336630356130 6 | 62376432636664306532353631393935626339663565376431353064626438323762373361613130 7 | 3239343165336165363731636563663938353664346230333331 8 | -------------------------------------------------------------------------------- /group_vars/routers.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 31626161366138616431333639653430643932633961373433636433616330623364386332663934 3 | 3161613633336662363032663736393262326638323561300a333236313563303536356331306239 4 | 36373836343461343066373231613564366533626434663939656132316136653637653038393063 5 | 6262333933313965350a373938326236393632636230393331373362346563646132313563633666 6 | 32653363383732323136393465653631373961303439616534363930373530636633393963633831 7 | 33323762396538666534353039313936613335323363663366393066366131626463653431646235 8 | 37333033643865356135376266316563363530343537303365313134393038613739643232336636 9 | 65613066303732343564 10 | -------------------------------------------------------------------------------- /group_vars/switches.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 62356664316661333630356137363039663339653566393934646639363038646435356235383837 3 | 3766396136343862663962326638643261333032663933300a623466396636306264643630613066 4 | 61626633326164643535353834653938633730646262313636623966373638346638643231353563 5 | 3562393831626537360a343334616337626330333664333836313836626635333831376362633861 6 | 37316264633665646639336536613536646334656230653061306331303264333738616138353132 7 | 62386339373664316663356231613031313737323739333663666436383636376231356266346234 8 | 62653731646539383236373239383938313936626462633061346365393465613164373433636433 9 | 64626130303264376633 10 | -------------------------------------------------------------------------------- /host_vars/veos01.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 30356232356635643337353332346131663434376364336262646337616662623630393437363130 3 | 6130623563333139646137653931653166333736353431620a303839356462363437343963393837 4 | 31383432373736633032366162666437343133633365323437303363373439303932343663363563 5 | 3364323163346539630a643038636530663065626565343639386363326634336332643465333035 6 | 31643063626266343932303632356636656537376539613063633665663435393262 7 | -------------------------------------------------------------------------------- /host_vars/veos02.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 64666633643636316166616663346262353464393437666232323439653363326230396564313865 3 | 3133613366306466393365626231366631326663623563610a393835306237653536643036323239 4 | 35356431373633353066393663313533353964323066633836323864386162376563363432666431 5 | 3765653239383565660a383233393862646234373532386233356164346466376465303337613831 6 | 30613139613363323664316566336338633837623731633838386234346561623235 7 | -------------------------------------------------------------------------------- /netbox_inventory.yml: -------------------------------------------------------------------------------- 1 | --- 2 | plugin: netbox.netbox.nb_inventory 3 | api_endpoint: http://netbox03 4 | validate_certs: false 5 | config_context: false 6 | group_by: 7 | - device_roles 8 | - platforms 9 | compose: 10 | ansible_network_os: platform.slug 11 | query_filters: 12 | - site: "minnesota01" 13 | - has_primary_ip: True 14 | -------------------------------------------------------------------------------- /plugins/filter/filter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ 3 | Author: Josh VanDeraa 4 | 5 | Filters related to the Ansible Config testing Playbooks 6 | """ 7 | # pylint disable=R0911 8 | import requests 9 | 10 | 11 | class FilterModule: 12 | """ 13 | Defines a filter module object. 14 | """ 15 | 16 | @staticmethod 17 | def filters(): 18 | """ 19 | Return a list of hashes where the key is the filter 20 | name exposed to playbooks and the value is the function. 21 | """ 22 | return { 23 | "get_ciscoios_serial_list": FilterModule.get_ciscoios_serial_list, 24 | "compare_lists": FilterModule.compare_lists, 25 | "convert_mac_address": FilterModule.convert_mac_address, 26 | "get_role_from_hostname": FilterModule.get_role_from_hostname, 27 | "get_interface_type": FilterModule.get_interface_type, 28 | "get_interface_by_bandwidth": FilterModule.get_interface_by_bandwidth, 29 | "build_ipv4_from_facts": FilterModule.build_ipv4_from_facts, 30 | "get_interface_id": FilterModule.get_interface_id, 31 | } 32 | 33 | @staticmethod 34 | def get_ciscoios_serial_list(ansible_facts): 35 | """ 36 | Goal: Take information in Ansible Facts and return a list of serial numbers for the logical 37 | device. If it is a stack of switches with a single management, this should return multiple 38 | serial numbers. If it is a single unit then it should return the single serial number within 39 | a list. 40 | 41 | ansible_net_serialnum (string) The serial number of the remote device 42 | 43 | ansible_net_stacked_serialnums (list) when multiple devices are configured in a stack 44 | The serial numbers of each device in the stack 45 | 46 | """ 47 | # Check if net_stacked_serialnums exists, then return it 48 | if ansible_facts.get("net_stacked_serialnums") is not None: 49 | return ansible_facts.get("net_stacked_serialnums") 50 | 51 | # Check that net_serialnum exists, returning the string back as a list of one 52 | if ansible_facts.get("net_serialnum") is not None: 53 | return [ansible_facts.get("net_serialnum")] 54 | 55 | return None 56 | 57 | @staticmethod 58 | def convert_mac_address(mac_address): 59 | """ 60 | Goal: Return all MAC addresses sent in as a type of EAU-48 Address type 61 | Default: If None or 'None' is sent in as the Mac address, then all 0s are returned 62 | A method to verify that the MAC address is in a valid format for Netbox 63 | and returns a 00:00:00:00:00:00 if the MAC address is of type None 64 | 65 | EUI48 : AB:CD:EF:01:23:45 66 | "Cisco" : abcd.ef01.2345 67 | "Microsoft": AB-CD-EF-01-23-45 68 | """ 69 | if mac_address in ("None", None): 70 | mac_address = "0000.0000.0000" 71 | 72 | mac_address = mac_address.upper() 73 | mac_address = mac_address.replace(".", "") 74 | mac_address = mac_address.replace("-", "") 75 | mac_address = mac_address.replace(":", "") 76 | mac_address = ":".join( 77 | [mac_address[i : i + 2] for i, j in enumerate(mac_address) if not i % 2] 78 | ) 79 | 80 | return mac_address 81 | 82 | @staticmethod 83 | def compare_lists(device_serial_numbers, netbox_serial_numbers): 84 | """ 85 | A method to verify that the serial number(s) of a device/virtual chassis 86 | are in fact the same 87 | """ 88 | device_serials_set = set(device_serial_numbers) 89 | netbox_serials_set = set(netbox_serial_numbers) 90 | 91 | return device_serials_set == netbox_serials_set 92 | 93 | @staticmethod 94 | def get_role_from_hostname(hostname): 95 | """ 96 | Method to get a role from the hostname 97 | 98 | Args: 99 | hostname (string): String representation of the hostname 100 | """ 101 | if "sw" in hostname: 102 | return "Switch" 103 | 104 | if "rtr" in hostname: 105 | return "Router" 106 | 107 | if "fw" in hostname: 108 | return "Firewall" 109 | 110 | if "nxos" in hostname: 111 | return "Switch" 112 | 113 | if "veos" in hostname: 114 | return "Switch" 115 | 116 | return None 117 | 118 | @staticmethod 119 | def get_interface_by_bandwidth(bandwidth_value): 120 | """ 121 | Method to get the interface type based on the bandwidth value 122 | 123 | Args: 124 | bandwidth_value (int): Integer of the bandwith speed 125 | """ 126 | if bandwidth_value == 8000000: 127 | return "Virtual" 128 | 129 | if bandwidth_value == 1000000: 130 | return "1000base-t" 131 | 132 | if bandwidth_value == 10000000: 133 | return "10gbase-t" 134 | 135 | return None 136 | 137 | @staticmethod 138 | def build_ipv4_from_facts(ansible_facts): 139 | """ 140 | Method to build IPv4 CIDR address from the Ansible Facts output: 141 | 142 | "GigabitEthernet0/0": { 143 | "bandwidth": 1000000, 144 | "description": null, 145 | "duplex": "Auto", 146 | "ipv4": [ 147 | { 148 | "address": "192.168.0.167", 149 | "subnet": "24" 150 | } 151 | ], 152 | "lineprotocol": "up", 153 | "macaddress": "0c25.ea08.4900", 154 | "mediatype": "RJ45", 155 | "mtu": 1500, 156 | "operstatus": "up", 157 | "type": "iGbE" 158 | }, 159 | "GigabitEthernet0/1": { 160 | "bandwidth": 1000000, 161 | "description": null, 162 | "duplex": "Auto", 163 | "ipv4": [ 164 | { 165 | "address": "172.31.1.1", 166 | "subnet": "24" 167 | } 168 | ], 169 | "lineprotocol": "up", 170 | "macaddress": "0c25.ea08.4901", 171 | "mediatype": "RJ45", 172 | "mtu": 1500, 173 | "operstatus": "up", 174 | "type": "iGbE" 175 | }, 176 | 177 | ======================================================================== 178 | 179 | Returns: [ 180 | {"interface": "GigabitEthernet0/0", "address": "192.168.0.167/24"}, 181 | {"interface": "GigabitEthernet0/1", "address": "172.31.1.1/24"} 182 | ] 183 | 184 | [summary] 185 | Args: 186 | ipv4_dict (dict): Dictionary from Ansible Facts representing addresses 187 | """ 188 | return_list = [] 189 | interface_facts = ansible_facts.get("net_interfaces") 190 | 191 | if interface_facts is not None: 192 | for interface, data in interface_facts.items(): 193 | ipv4 = data.get("ipv4", None) 194 | if ipv4 is None: 195 | continue 196 | 197 | # IOS Switch Output 198 | if isinstance(ipv4, list): 199 | for prefix in ipv4: 200 | prefix_len = prefix.get("subnet", None) 201 | if prefix_len is None: 202 | prefix_len = prefix.get("masklen") 203 | cidr = f"{prefix['address']}/{prefix_len}" 204 | return_list.append({"interface": interface, "address": cidr}) 205 | 206 | # NXOS Switch output 207 | if isinstance(ipv4, dict): 208 | if ipv4 == {}: 209 | continue 210 | cidr = f"{ipv4['address']}/{ipv4['masklen']}" 211 | return_list.append({"interface": interface, "address": cidr}) 212 | 213 | return return_list 214 | 215 | @staticmethod 216 | def get_interface_id(vm_name, netbox_headers): 217 | """ 218 | Get an Interface ID from Netbox 219 | 220 | Args: 221 | vm_name (string): Name of the VM 222 | """ 223 | url = ( 224 | "http://netbox03/api/virtualization/interfaces/?virtual_machine=%s" 225 | % vm_name 226 | ) 227 | result = requests.get(url, headers=netbox_headers) 228 | return result.json()["results"][0]["id"] 229 | 230 | @staticmethod 231 | def get_interface_type(interface_name): 232 | """ 233 | Method to return the interface type (Physical, Virtual, speed) based on the name of the 234 | interface 235 | 236 | Args: 237 | interface_name (string): Interface Name 238 | """ 239 | if "eth" in interface_name: 240 | return "1000base-t" 241 | 242 | if "Gigabit" in interface_name: 243 | return "1000base-t" 244 | 245 | if "Ethernet" in interface_name: 246 | return "1000base-t" 247 | 248 | if "switch0" in interface_name: 249 | return "Virtual" 250 | 251 | if "lo" in interface_name: 252 | return "Virtual" 253 | 254 | if "Loopback" in interface_name: 255 | return "Virtual" 256 | 257 | if "vlan" in interface_name.lower(): 258 | return "Virtual" 259 | 260 | if "Port-channel" in interface_name: 261 | return "LAG" 262 | 263 | if "mgmt0" in interface_name: 264 | return "1000base-t" 265 | 266 | if "Management1" in interface_name: 267 | return "1000base-t" 268 | 269 | return None 270 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_paths = ./ 3 | adopts = -vv 4 | -------------------------------------------------------------------------------- /requirements.system: -------------------------------------------------------------------------------- 1 | build-essential 2 | git 3 | iputils-ping 4 | openssh-client 5 | vim -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ansible 2 | ansible-lint 3 | bandit 4 | black 5 | paramiko 6 | pylint 7 | pynetbox==5.0.5 8 | pytest 9 | pytest-pythonpath 10 | textfsm 11 | yamllint -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | collections: 3 | - name: netbox.netbox 4 | version: 1.0.0 5 | 6 | roles: 7 | - name: ansible-network.network-engine 8 | -------------------------------------------------------------------------------- /tests/pb.unittest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Test parsers to verify functionality 3 | - name: "PLAY 1: TEST CUSTOM FILTERS" 4 | hosts: localhost 5 | connection: local 6 | tasks: 7 | # All test files, which are lists of ansible tasks, begin with the 8 | # string "test_" and end with ".yml" and are contained in the "tasks/" 9 | # directory. These specify individual test cases per function. 10 | - name: "TASK 1: SYS >> STORE TASK FILE SEARCH STRING" 11 | set_fact: 12 | TASKS: "test_*[.]yml" 13 | 14 | # Perform a "find" to get a list of these files. Note that for specific 15 | # unit tests, the "--extra-vars" option can be used at the shell to 16 | # override the "TASKS" variable to change the regex for matching. 17 | # This is uncommonly used and should be avoided for general purpose tests. 18 | - name: "SYS >> GET FILES MATCHING TASKS" 19 | find: 20 | path: "{{ playbook_dir }}/tasks" 21 | patterns: "test_*[.]yml" 22 | register: "files_to_test" 23 | 24 | # Build a list of strings with only the file paths to simplify 25 | # future iteration and troubleshooting. 26 | - name: "SYS >> ASSEMBLE FILE PATHS INTO A SINGLE LIST" 27 | set_fact: 28 | file_list: "{{ files_to_test.files | map(attribute='path') | list }}" 29 | 30 | # Print the discovered file paths to ensure the regex matched. 31 | - name: "DEBUG >> PRINT FILE PATHS" 32 | debug: 33 | var: "file_list" 34 | 35 | # Iteratively include each file (sequence does not matter) and run 36 | # the tests included in that file. 37 | - include_tasks: "{{ item }}" 38 | loop: "{{ file_list }}" 39 | ... 40 | -------------------------------------------------------------------------------- /tests/tasks/test_task.unittest.macaddress.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "MACADDRESS: STORE TEST DATA" 3 | set_fact: 4 | mac1: 123a.124a.2387 5 | mac2: 0c98.53d9.d800 6 | mac3: 0C98.53D9.D802 7 | mac4: 0c-98-53-d9-d8-02 8 | mac5: 0C-98-53-D9-D8-02 9 | bad1: None 10 | 11 | - name: "MACADDRESS: PERFORM FILTER" 12 | set_fact: 13 | mac1_data: "{{ mac1 | convert_mac_address }}" 14 | mac2_data: "{{ mac2 | convert_mac_address }}" 15 | mac3_data: "{{ mac3 | convert_mac_address }}" 16 | mac4_data: "{{ mac4 | convert_mac_address }}" 17 | mac5_data: "{{ mac5 | convert_mac_address }}" 18 | bad1_data: "{{ bad1 | convert_mac_address }}" 19 | 20 | - name: "MACADDRESS: PRINT DATA" 21 | debug: 22 | msg: "{{ item }}" 23 | with_items: 24 | - "{{ mac1_data }}" 25 | - "{{ mac2_data }}" 26 | - "{{ mac3_data }}" 27 | - "{{ mac4_data }}" 28 | - "{{ mac5_data }}" 29 | - "{{ bad1_data }}" 30 | 31 | - name: "MACADDRESS: VERIFY RESULTS" 32 | assert: 33 | that: 34 | - "mac1_data == '12:3A:12:4A:23:87'" 35 | - "mac2_data == '0C:98:53:D9:D8:00'" 36 | - "mac3_data == '0C:98:53:D9:D8:02'" 37 | - "mac4_data == '0C:98:53:D9:D8:02'" 38 | - "mac5_data == '0C:98:53:D9:D8:02'" 39 | - "bad1_data == '00:00:00:00:00:00'" 40 | ... 41 | -------------------------------------------------------------------------------- /tests/tasks/test_task.unittest.netbox_device_compare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "COMPARE_SERIALS: STORE TEST DATA" 3 | set_fact: 4 | single_device: ["99JCD34YT4C"] 5 | single_netbox: ["99JCD34YT4C"] 6 | single_device_fail: ["99JCD34YT4D"] 7 | single_netbox_fail: ["99JCD34YT4C"] 8 | multiple_devices: ["99JCD34YT4C", "adsfaafdaszffas"] 9 | multiple_netbox: ["adsfaafdaszffas", "99JCD34YT4C"] 10 | multiple_devices_fail: ["99JCD34YT4C", "adsfaafdaszffas"] 11 | multiple_netbox_fail: ["safdaszffas", "99JCD34YT4C"] 12 | 13 | - name: "COMPARE_SERIALS: PERFORM FILTER" 14 | set_fact: 15 | single_device_result: "{{ single_device | compare_lists(single_netbox) }}" 16 | single_device_fail_result: "{{ single_device_fail | compare_lists(single_netbox_fail) }}" 17 | multiple_device_result: "{{ multiple_devices | compare_lists(multiple_netbox) }}" 18 | multiple_device_fail_result: "{{ multiple_devices_fail | compare_lists(multiple_netbox_fail) }}" 19 | 20 | - name: "COMPARE_SERIALS: VERIFY RESULTS" 21 | assert: 22 | that: 23 | - "single_device_result" 24 | - "single_device_fail_result == False" 25 | - "multiple_device_result" 26 | - "multiple_device_fail_result == False" 27 | ... 28 | -------------------------------------------------------------------------------- /tests/test_build_ipv4_from_facts.py: -------------------------------------------------------------------------------- 1 | """ 2 | Unit test for building ipv4 from facts 3 | """ 4 | from plugins.filter.filter import FilterModule 5 | 6 | def test_build_ipv4_from_facts(): 7 | """ 8 | Unit test of building ipv4 facts 9 | """ 10 | null = None 11 | nxos_data = { 12 | "discovered_interpreter_python": "/usr/local/bin/python3.7", 13 | "net__hostname": "nxos01", 14 | "net__os": "7.3(0)D1(1)", 15 | "net__platform": "NX-OSv Chassis", 16 | "net_all_ipv4_addresses": [ 17 | "172.31.1.101" 18 | ], 19 | "net_all_ipv6_addresses": [], 20 | "net_api": "cliconf", 21 | "net_fan_info": [ 22 | { 23 | "hw_ver": "0.0", 24 | "model": null, 25 | "name": "ChassisFan1", 26 | "status": "Ok" 27 | }, 28 | { 29 | "hw_ver": "0.0", 30 | "model": null, 31 | "name": "ChassisFan2", 32 | "status": "None" 33 | }, 34 | { 35 | "hw_ver": "--", 36 | "model": "--", 37 | "name": "Fan_in_PS1", 38 | "status": "Ok" 39 | }, 40 | { 41 | "hw_ver": "--", 42 | "model": "--", 43 | "name": "Fan_in_PS2", 44 | "status": "Failure" 45 | } 46 | ], 47 | "net_features_enabled": [ 48 | "privilege" 49 | ], 50 | "net_filesystems": [ 51 | "bootflash:" 52 | ], 53 | "net_gather_network_resources": [], 54 | "net_gather_subset": [ 55 | "hardware", 56 | "default", 57 | "legacy", 58 | "interfaces", 59 | "features" 60 | ], 61 | "net_hostname": "nxos01", 62 | "net_image": "bootflash:///titanium-d1.7.3.0.D1.1.bin", 63 | "net_interfaces": { 64 | "Ethernet2/1": { 65 | "bandwidth": 1000000, 66 | "duplex": "auto", 67 | "macaddress": "0000.0000.002f", 68 | "mode": "routed", 69 | "mtu": "1500", 70 | "speed": "auto-speed", 71 | "state": "down", 72 | "type": "Ethernet" 73 | }, 74 | "Ethernet2/10": { 75 | "bandwidth": 1000000, 76 | "duplex": "auto", 77 | "macaddress": "0000.0000.002f", 78 | "mode": "routed", 79 | "mtu": "1500", 80 | "speed": "auto-speed", 81 | "state": "down", 82 | "type": "Ethernet" 83 | }, 84 | "Ethernet2/11": { 85 | "bandwidth": 1000000, 86 | "duplex": "auto", 87 | "macaddress": "0000.0000.002f", 88 | "mode": "routed", 89 | "mtu": "1500", 90 | "speed": "auto-speed", 91 | "state": "down", 92 | "type": "Ethernet" 93 | }, 94 | "Ethernet2/12": { 95 | "bandwidth": 1000000, 96 | "duplex": "auto", 97 | "macaddress": "0000.0000.002f", 98 | "mode": "routed", 99 | "mtu": "1500", 100 | "speed": "auto-speed", 101 | "state": "down", 102 | "type": "Ethernet" 103 | }, 104 | "Ethernet2/13": { 105 | "bandwidth": 1000000, 106 | "duplex": "auto", 107 | "macaddress": "0000.0000.002f", 108 | "mode": "routed", 109 | "mtu": "1500", 110 | "speed": "auto-speed", 111 | "state": "down", 112 | "type": "Ethernet" 113 | }, 114 | "Ethernet2/14": { 115 | "bandwidth": 1000000, 116 | "duplex": "auto", 117 | "macaddress": "0000.0000.002f", 118 | "mode": "routed", 119 | "mtu": "1500", 120 | "speed": "auto-speed", 121 | "state": "down", 122 | "type": "Ethernet" 123 | }, 124 | "Ethernet2/15": { 125 | "bandwidth": 1000000, 126 | "duplex": "auto", 127 | "macaddress": "0000.0000.002f", 128 | "mode": "routed", 129 | "mtu": "1500", 130 | "speed": "auto-speed", 131 | "state": "down", 132 | "type": "Ethernet" 133 | }, 134 | "Ethernet2/16": { 135 | "bandwidth": 1000000, 136 | "duplex": "auto", 137 | "macaddress": "0000.0000.002f", 138 | "mode": "routed", 139 | "mtu": "1500", 140 | "speed": "auto-speed", 141 | "state": "down", 142 | "type": "Ethernet" 143 | }, 144 | "Ethernet2/17": { 145 | "bandwidth": 1000000, 146 | "duplex": "auto", 147 | "macaddress": "0000.0000.002f", 148 | "mode": "routed", 149 | "mtu": "1500", 150 | "speed": "auto-speed", 151 | "state": "down", 152 | "type": "Ethernet" 153 | }, 154 | "Ethernet2/18": { 155 | "bandwidth": 1000000, 156 | "duplex": "auto", 157 | "macaddress": "0000.0000.002f", 158 | "mode": "routed", 159 | "mtu": "1500", 160 | "speed": "auto-speed", 161 | "state": "down", 162 | "type": "Ethernet" 163 | }, 164 | "Ethernet2/19": { 165 | "bandwidth": 1000000, 166 | "duplex": "auto", 167 | "macaddress": "0000.0000.002f", 168 | "mode": "routed", 169 | "mtu": "1500", 170 | "speed": "auto-speed", 171 | "state": "down", 172 | "type": "Ethernet" 173 | }, 174 | "Ethernet2/2": { 175 | "bandwidth": 1000000, 176 | "duplex": "auto", 177 | "macaddress": "0000.0000.002f", 178 | "mode": "routed", 179 | "mtu": "1500", 180 | "speed": "auto-speed", 181 | "state": "down", 182 | "type": "Ethernet" 183 | }, 184 | "Ethernet2/20": { 185 | "bandwidth": 1000000, 186 | "duplex": "auto", 187 | "macaddress": "0000.0000.002f", 188 | "mode": "routed", 189 | "mtu": "1500", 190 | "speed": "auto-speed", 191 | "state": "down", 192 | "type": "Ethernet" 193 | }, 194 | "Ethernet2/21": { 195 | "bandwidth": 1000000, 196 | "duplex": "auto", 197 | "macaddress": "0000.0000.002f", 198 | "mode": "routed", 199 | "mtu": "1500", 200 | "speed": "auto-speed", 201 | "state": "down", 202 | "type": "Ethernet" 203 | }, 204 | "Ethernet2/22": { 205 | "bandwidth": 1000000, 206 | "duplex": "auto", 207 | "macaddress": "0000.0000.002f", 208 | "mode": "routed", 209 | "mtu": "1500", 210 | "speed": "auto-speed", 211 | "state": "down", 212 | "type": "Ethernet" 213 | }, 214 | "Ethernet2/23": { 215 | "bandwidth": 1000000, 216 | "duplex": "auto", 217 | "macaddress": "0000.0000.002f", 218 | "mode": "routed", 219 | "mtu": "1500", 220 | "speed": "auto-speed", 221 | "state": "down", 222 | "type": "Ethernet" 223 | }, 224 | "Ethernet2/24": { 225 | "bandwidth": 1000000, 226 | "duplex": "auto", 227 | "macaddress": "0000.0000.002f", 228 | "mode": "routed", 229 | "mtu": "1500", 230 | "speed": "auto-speed", 231 | "state": "down", 232 | "type": "Ethernet" 233 | }, 234 | "Ethernet2/25": { 235 | "bandwidth": 1000000, 236 | "duplex": "auto", 237 | "macaddress": "0000.0000.002f", 238 | "mode": "routed", 239 | "mtu": "1500", 240 | "speed": "auto-speed", 241 | "state": "down", 242 | "type": "Ethernet" 243 | }, 244 | "Ethernet2/26": { 245 | "bandwidth": 1000000, 246 | "duplex": "auto", 247 | "macaddress": "0000.0000.002f", 248 | "mode": "routed", 249 | "mtu": "1500", 250 | "speed": "auto-speed", 251 | "state": "down", 252 | "type": "Ethernet" 253 | }, 254 | "Ethernet2/27": { 255 | "bandwidth": 1000000, 256 | "duplex": "auto", 257 | "macaddress": "0000.0000.002f", 258 | "mode": "routed", 259 | "mtu": "1500", 260 | "speed": "auto-speed", 261 | "state": "down", 262 | "type": "Ethernet" 263 | }, 264 | "Ethernet2/28": { 265 | "bandwidth": 1000000, 266 | "duplex": "auto", 267 | "macaddress": "0000.0000.002f", 268 | "mode": "routed", 269 | "mtu": "1500", 270 | "speed": "auto-speed", 271 | "state": "down", 272 | "type": "Ethernet" 273 | }, 274 | "Ethernet2/29": { 275 | "bandwidth": 1000000, 276 | "duplex": "auto", 277 | "macaddress": "0000.0000.002f", 278 | "mode": "routed", 279 | "mtu": "1500", 280 | "speed": "auto-speed", 281 | "state": "down", 282 | "type": "Ethernet" 283 | }, 284 | "Ethernet2/3": { 285 | "bandwidth": 1000000, 286 | "duplex": "auto", 287 | "macaddress": "0000.0000.002f", 288 | "mode": "routed", 289 | "mtu": "1500", 290 | "speed": "auto-speed", 291 | "state": "down", 292 | "type": "Ethernet" 293 | }, 294 | "Ethernet2/30": { 295 | "bandwidth": 1000000, 296 | "duplex": "auto", 297 | "macaddress": "0000.0000.002f", 298 | "mode": "routed", 299 | "mtu": "1500", 300 | "speed": "auto-speed", 301 | "state": "down", 302 | "type": "Ethernet" 303 | }, 304 | "Ethernet2/31": { 305 | "bandwidth": 1000000, 306 | "duplex": "auto", 307 | "macaddress": "0000.0000.002f", 308 | "mode": "routed", 309 | "mtu": "1500", 310 | "speed": "auto-speed", 311 | "state": "down", 312 | "type": "Ethernet" 313 | }, 314 | "Ethernet2/32": { 315 | "bandwidth": 1000000, 316 | "duplex": "auto", 317 | "macaddress": "0000.0000.002f", 318 | "mode": "routed", 319 | "mtu": "1500", 320 | "speed": "auto-speed", 321 | "state": "down", 322 | "type": "Ethernet" 323 | }, 324 | "Ethernet2/33": { 325 | "bandwidth": 1000000, 326 | "duplex": "auto", 327 | "macaddress": "0000.0000.002f", 328 | "mode": "routed", 329 | "mtu": "1500", 330 | "speed": "auto-speed", 331 | "state": "down", 332 | "type": "Ethernet" 333 | }, 334 | "Ethernet2/34": { 335 | "bandwidth": 1000000, 336 | "duplex": "auto", 337 | "macaddress": "0000.0000.002f", 338 | "mode": "routed", 339 | "mtu": "1500", 340 | "speed": "auto-speed", 341 | "state": "down", 342 | "type": "Ethernet" 343 | }, 344 | "Ethernet2/35": { 345 | "bandwidth": 1000000, 346 | "duplex": "auto", 347 | "macaddress": "0000.0000.002f", 348 | "mode": "routed", 349 | "mtu": "1500", 350 | "speed": "auto-speed", 351 | "state": "down", 352 | "type": "Ethernet" 353 | }, 354 | "Ethernet2/36": { 355 | "bandwidth": 1000000, 356 | "duplex": "auto", 357 | "macaddress": "0000.0000.002f", 358 | "mode": "routed", 359 | "mtu": "1500", 360 | "speed": "auto-speed", 361 | "state": "down", 362 | "type": "Ethernet" 363 | }, 364 | "Ethernet2/37": { 365 | "bandwidth": 1000000, 366 | "duplex": "auto", 367 | "macaddress": "0000.0000.002f", 368 | "mode": "routed", 369 | "mtu": "1500", 370 | "speed": "auto-speed", 371 | "state": "down", 372 | "type": "Ethernet" 373 | }, 374 | "Ethernet2/38": { 375 | "bandwidth": 1000000, 376 | "duplex": "auto", 377 | "macaddress": "0000.0000.002f", 378 | "mode": "routed", 379 | "mtu": "1500", 380 | "speed": "auto-speed", 381 | "state": "down", 382 | "type": "Ethernet" 383 | }, 384 | "Ethernet2/39": { 385 | "bandwidth": 1000000, 386 | "duplex": "auto", 387 | "macaddress": "0000.0000.002f", 388 | "mode": "routed", 389 | "mtu": "1500", 390 | "speed": "auto-speed", 391 | "state": "down", 392 | "type": "Ethernet" 393 | }, 394 | "Ethernet2/4": { 395 | "bandwidth": 1000000, 396 | "duplex": "auto", 397 | "macaddress": "0000.0000.002f", 398 | "mode": "routed", 399 | "mtu": "1500", 400 | "speed": "auto-speed", 401 | "state": "down", 402 | "type": "Ethernet" 403 | }, 404 | "Ethernet2/40": { 405 | "bandwidth": 1000000, 406 | "duplex": "auto", 407 | "macaddress": "0000.0000.002f", 408 | "mode": "routed", 409 | "mtu": "1500", 410 | "speed": "auto-speed", 411 | "state": "down", 412 | "type": "Ethernet" 413 | }, 414 | "Ethernet2/41": { 415 | "bandwidth": 1000000, 416 | "duplex": "auto", 417 | "macaddress": "0000.0000.002f", 418 | "mode": "routed", 419 | "mtu": "1500", 420 | "speed": "auto-speed", 421 | "state": "down", 422 | "type": "Ethernet" 423 | }, 424 | "Ethernet2/42": { 425 | "bandwidth": 1000000, 426 | "duplex": "auto", 427 | "macaddress": "0000.0000.002f", 428 | "mode": "routed", 429 | "mtu": "1500", 430 | "speed": "auto-speed", 431 | "state": "down", 432 | "type": "Ethernet" 433 | }, 434 | "Ethernet2/43": { 435 | "bandwidth": 1000000, 436 | "duplex": "auto", 437 | "macaddress": "0000.0000.002f", 438 | "mode": "routed", 439 | "mtu": "1500", 440 | "speed": "auto-speed", 441 | "state": "down", 442 | "type": "Ethernet" 443 | }, 444 | "Ethernet2/44": { 445 | "bandwidth": 1000000, 446 | "duplex": "auto", 447 | "macaddress": "0000.0000.002f", 448 | "mode": "routed", 449 | "mtu": "1500", 450 | "speed": "auto-speed", 451 | "state": "down", 452 | "type": "Ethernet" 453 | }, 454 | "Ethernet2/45": { 455 | "bandwidth": 1000000, 456 | "duplex": "auto", 457 | "macaddress": "0000.0000.002f", 458 | "mode": "routed", 459 | "mtu": "1500", 460 | "speed": "auto-speed", 461 | "state": "down", 462 | "type": "Ethernet" 463 | }, 464 | "Ethernet2/46": { 465 | "bandwidth": 1000000, 466 | "duplex": "auto", 467 | "macaddress": "0000.0000.002f", 468 | "mode": "routed", 469 | "mtu": "1500", 470 | "speed": "auto-speed", 471 | "state": "down", 472 | "type": "Ethernet" 473 | }, 474 | "Ethernet2/47": { 475 | "bandwidth": 1000000, 476 | "duplex": "auto", 477 | "macaddress": "0000.0000.002f", 478 | "mode": "routed", 479 | "mtu": "1500", 480 | "speed": "auto-speed", 481 | "state": "down", 482 | "type": "Ethernet" 483 | }, 484 | "Ethernet2/48": { 485 | "bandwidth": 1000000, 486 | "duplex": "auto", 487 | "macaddress": "0000.0000.002f", 488 | "mode": "routed", 489 | "mtu": "1500", 490 | "speed": "auto-speed", 491 | "state": "down", 492 | "type": "Ethernet" 493 | }, 494 | "Ethernet2/5": { 495 | "bandwidth": 1000000, 496 | "duplex": "auto", 497 | "macaddress": "0000.0000.002f", 498 | "mode": "routed", 499 | "mtu": "1500", 500 | "speed": "auto-speed", 501 | "state": "down", 502 | "type": "Ethernet" 503 | }, 504 | "Ethernet2/6": { 505 | "bandwidth": 1000000, 506 | "duplex": "auto", 507 | "macaddress": "0000.0000.002f", 508 | "mode": "routed", 509 | "mtu": "1500", 510 | "speed": "auto-speed", 511 | "state": "down", 512 | "type": "Ethernet" 513 | }, 514 | "Ethernet2/7": { 515 | "bandwidth": 1000000, 516 | "duplex": "auto", 517 | "macaddress": "0000.0000.002f", 518 | "mode": "routed", 519 | "mtu": "1500", 520 | "speed": "auto-speed", 521 | "state": "down", 522 | "type": "Ethernet" 523 | }, 524 | "Ethernet2/8": { 525 | "bandwidth": 1000000, 526 | "duplex": "auto", 527 | "macaddress": "0000.0000.002f", 528 | "mode": "routed", 529 | "mtu": "1500", 530 | "speed": "auto-speed", 531 | "state": "down", 532 | "type": "Ethernet" 533 | }, 534 | "Ethernet2/9": { 535 | "bandwidth": 1000000, 536 | "duplex": "auto", 537 | "macaddress": "0000.0000.002f", 538 | "mode": "routed", 539 | "mtu": "1500", 540 | "speed": "auto-speed", 541 | "state": "down", 542 | "type": "Ethernet" 543 | }, 544 | "Ethernet3/1": { 545 | "bandwidth": 1000000, 546 | "duplex": "auto", 547 | "macaddress": "0000.0000.002f", 548 | "mode": "routed", 549 | "mtu": "1500", 550 | "speed": "auto-speed", 551 | "state": "down", 552 | "type": "Ethernet" 553 | }, 554 | "Ethernet3/10": { 555 | "bandwidth": 1000000, 556 | "duplex": "auto", 557 | "macaddress": "0000.0000.002f", 558 | "mode": "routed", 559 | "mtu": "1500", 560 | "speed": "auto-speed", 561 | "state": "down", 562 | "type": "Ethernet" 563 | }, 564 | "Ethernet3/11": { 565 | "bandwidth": 1000000, 566 | "duplex": "auto", 567 | "macaddress": "0000.0000.002f", 568 | "mode": "routed", 569 | "mtu": "1500", 570 | "speed": "auto-speed", 571 | "state": "down", 572 | "type": "Ethernet" 573 | }, 574 | "Ethernet3/12": { 575 | "bandwidth": 1000000, 576 | "duplex": "auto", 577 | "macaddress": "0000.0000.002f", 578 | "mode": "routed", 579 | "mtu": "1500", 580 | "speed": "auto-speed", 581 | "state": "down", 582 | "type": "Ethernet" 583 | }, 584 | "Ethernet3/13": { 585 | "bandwidth": 1000000, 586 | "duplex": "auto", 587 | "macaddress": "0000.0000.002f", 588 | "mode": "routed", 589 | "mtu": "1500", 590 | "speed": "auto-speed", 591 | "state": "down", 592 | "type": "Ethernet" 593 | }, 594 | "Ethernet3/14": { 595 | "bandwidth": 1000000, 596 | "duplex": "auto", 597 | "macaddress": "0000.0000.002f", 598 | "mode": "routed", 599 | "mtu": "1500", 600 | "speed": "auto-speed", 601 | "state": "down", 602 | "type": "Ethernet" 603 | }, 604 | "Ethernet3/15": { 605 | "bandwidth": 1000000, 606 | "duplex": "auto", 607 | "macaddress": "0000.0000.002f", 608 | "mode": "routed", 609 | "mtu": "1500", 610 | "speed": "auto-speed", 611 | "state": "down", 612 | "type": "Ethernet" 613 | }, 614 | "Ethernet3/16": { 615 | "bandwidth": 1000000, 616 | "duplex": "auto", 617 | "macaddress": "0000.0000.002f", 618 | "mode": "routed", 619 | "mtu": "1500", 620 | "speed": "auto-speed", 621 | "state": "down", 622 | "type": "Ethernet" 623 | }, 624 | "Ethernet3/17": { 625 | "bandwidth": 1000000, 626 | "duplex": "auto", 627 | "macaddress": "0000.0000.002f", 628 | "mode": "routed", 629 | "mtu": "1500", 630 | "speed": "auto-speed", 631 | "state": "down", 632 | "type": "Ethernet" 633 | }, 634 | "Ethernet3/18": { 635 | "bandwidth": 1000000, 636 | "duplex": "auto", 637 | "macaddress": "0000.0000.002f", 638 | "mode": "routed", 639 | "mtu": "1500", 640 | "speed": "auto-speed", 641 | "state": "down", 642 | "type": "Ethernet" 643 | }, 644 | "Ethernet3/19": { 645 | "bandwidth": 1000000, 646 | "duplex": "auto", 647 | "macaddress": "0000.0000.002f", 648 | "mode": "routed", 649 | "mtu": "1500", 650 | "speed": "auto-speed", 651 | "state": "down", 652 | "type": "Ethernet" 653 | }, 654 | "Ethernet3/2": { 655 | "bandwidth": 1000000, 656 | "duplex": "auto", 657 | "macaddress": "0000.0000.002f", 658 | "mode": "routed", 659 | "mtu": "1500", 660 | "speed": "auto-speed", 661 | "state": "down", 662 | "type": "Ethernet" 663 | }, 664 | "Ethernet3/20": { 665 | "bandwidth": 1000000, 666 | "duplex": "auto", 667 | "macaddress": "0000.0000.002f", 668 | "mode": "routed", 669 | "mtu": "1500", 670 | "speed": "auto-speed", 671 | "state": "down", 672 | "type": "Ethernet" 673 | }, 674 | "Ethernet3/21": { 675 | "bandwidth": 1000000, 676 | "duplex": "auto", 677 | "macaddress": "0000.0000.002f", 678 | "mode": "routed", 679 | "mtu": "1500", 680 | "speed": "auto-speed", 681 | "state": "down", 682 | "type": "Ethernet" 683 | }, 684 | "Ethernet3/22": { 685 | "bandwidth": 1000000, 686 | "duplex": "auto", 687 | "macaddress": "0000.0000.002f", 688 | "mode": "routed", 689 | "mtu": "1500", 690 | "speed": "auto-speed", 691 | "state": "down", 692 | "type": "Ethernet" 693 | }, 694 | "Ethernet3/23": { 695 | "bandwidth": 1000000, 696 | "duplex": "auto", 697 | "macaddress": "0000.0000.002f", 698 | "mode": "routed", 699 | "mtu": "1500", 700 | "speed": "auto-speed", 701 | "state": "down", 702 | "type": "Ethernet" 703 | }, 704 | "Ethernet3/24": { 705 | "bandwidth": 1000000, 706 | "duplex": "auto", 707 | "macaddress": "0000.0000.002f", 708 | "mode": "routed", 709 | "mtu": "1500", 710 | "speed": "auto-speed", 711 | "state": "down", 712 | "type": "Ethernet" 713 | }, 714 | "Ethernet3/25": { 715 | "bandwidth": 1000000, 716 | "duplex": "auto", 717 | "macaddress": "0000.0000.002f", 718 | "mode": "routed", 719 | "mtu": "1500", 720 | "speed": "auto-speed", 721 | "state": "down", 722 | "type": "Ethernet" 723 | }, 724 | "Ethernet3/26": { 725 | "bandwidth": 1000000, 726 | "duplex": "auto", 727 | "macaddress": "0000.0000.002f", 728 | "mode": "routed", 729 | "mtu": "1500", 730 | "speed": "auto-speed", 731 | "state": "down", 732 | "type": "Ethernet" 733 | }, 734 | "Ethernet3/27": { 735 | "bandwidth": 1000000, 736 | "duplex": "auto", 737 | "macaddress": "0000.0000.002f", 738 | "mode": "routed", 739 | "mtu": "1500", 740 | "speed": "auto-speed", 741 | "state": "down", 742 | "type": "Ethernet" 743 | }, 744 | "Ethernet3/28": { 745 | "bandwidth": 1000000, 746 | "duplex": "auto", 747 | "macaddress": "0000.0000.002f", 748 | "mode": "routed", 749 | "mtu": "1500", 750 | "speed": "auto-speed", 751 | "state": "down", 752 | "type": "Ethernet" 753 | }, 754 | "Ethernet3/29": { 755 | "bandwidth": 1000000, 756 | "duplex": "auto", 757 | "macaddress": "0000.0000.002f", 758 | "mode": "routed", 759 | "mtu": "1500", 760 | "speed": "auto-speed", 761 | "state": "down", 762 | "type": "Ethernet" 763 | }, 764 | "Ethernet3/3": { 765 | "bandwidth": 1000000, 766 | "duplex": "auto", 767 | "macaddress": "0000.0000.002f", 768 | "mode": "routed", 769 | "mtu": "1500", 770 | "speed": "auto-speed", 771 | "state": "down", 772 | "type": "Ethernet" 773 | }, 774 | "Ethernet3/30": { 775 | "bandwidth": 1000000, 776 | "duplex": "auto", 777 | "macaddress": "0000.0000.002f", 778 | "mode": "routed", 779 | "mtu": "1500", 780 | "speed": "auto-speed", 781 | "state": "down", 782 | "type": "Ethernet" 783 | }, 784 | "Ethernet3/31": { 785 | "bandwidth": 1000000, 786 | "duplex": "auto", 787 | "macaddress": "0000.0000.002f", 788 | "mode": "routed", 789 | "mtu": "1500", 790 | "speed": "auto-speed", 791 | "state": "down", 792 | "type": "Ethernet" 793 | }, 794 | "Ethernet3/32": { 795 | "bandwidth": 1000000, 796 | "duplex": "auto", 797 | "macaddress": "0000.0000.002f", 798 | "mode": "routed", 799 | "mtu": "1500", 800 | "speed": "auto-speed", 801 | "state": "down", 802 | "type": "Ethernet" 803 | }, 804 | "Ethernet3/33": { 805 | "bandwidth": 1000000, 806 | "duplex": "auto", 807 | "macaddress": "0000.0000.002f", 808 | "mode": "routed", 809 | "mtu": "1500", 810 | "speed": "auto-speed", 811 | "state": "down", 812 | "type": "Ethernet" 813 | }, 814 | "Ethernet3/34": { 815 | "bandwidth": 1000000, 816 | "duplex": "auto", 817 | "macaddress": "0000.0000.002f", 818 | "mode": "routed", 819 | "mtu": "1500", 820 | "speed": "auto-speed", 821 | "state": "down", 822 | "type": "Ethernet" 823 | }, 824 | "Ethernet3/35": { 825 | "bandwidth": 1000000, 826 | "duplex": "auto", 827 | "macaddress": "0000.0000.002f", 828 | "mode": "routed", 829 | "mtu": "1500", 830 | "speed": "auto-speed", 831 | "state": "down", 832 | "type": "Ethernet" 833 | }, 834 | "Ethernet3/36": { 835 | "bandwidth": 1000000, 836 | "duplex": "auto", 837 | "macaddress": "0000.0000.002f", 838 | "mode": "routed", 839 | "mtu": "1500", 840 | "speed": "auto-speed", 841 | "state": "down", 842 | "type": "Ethernet" 843 | }, 844 | "Ethernet3/37": { 845 | "bandwidth": 1000000, 846 | "duplex": "auto", 847 | "macaddress": "0000.0000.002f", 848 | "mode": "routed", 849 | "mtu": "1500", 850 | "speed": "auto-speed", 851 | "state": "down", 852 | "type": "Ethernet" 853 | }, 854 | "Ethernet3/38": { 855 | "bandwidth": 1000000, 856 | "duplex": "auto", 857 | "macaddress": "0000.0000.002f", 858 | "mode": "routed", 859 | "mtu": "1500", 860 | "speed": "auto-speed", 861 | "state": "down", 862 | "type": "Ethernet" 863 | }, 864 | "Ethernet3/39": { 865 | "bandwidth": 1000000, 866 | "duplex": "auto", 867 | "macaddress": "0000.0000.002f", 868 | "mode": "routed", 869 | "mtu": "1500", 870 | "speed": "auto-speed", 871 | "state": "down", 872 | "type": "Ethernet" 873 | }, 874 | "Ethernet3/4": { 875 | "bandwidth": 1000000, 876 | "duplex": "auto", 877 | "macaddress": "0000.0000.002f", 878 | "mode": "routed", 879 | "mtu": "1500", 880 | "speed": "auto-speed", 881 | "state": "down", 882 | "type": "Ethernet" 883 | }, 884 | "Ethernet3/40": { 885 | "bandwidth": 1000000, 886 | "duplex": "auto", 887 | "macaddress": "0000.0000.002f", 888 | "mode": "routed", 889 | "mtu": "1500", 890 | "speed": "auto-speed", 891 | "state": "down", 892 | "type": "Ethernet" 893 | }, 894 | "Ethernet3/41": { 895 | "bandwidth": 1000000, 896 | "duplex": "auto", 897 | "macaddress": "0000.0000.002f", 898 | "mode": "routed", 899 | "mtu": "1500", 900 | "speed": "auto-speed", 901 | "state": "down", 902 | "type": "Ethernet" 903 | }, 904 | "Ethernet3/42": { 905 | "bandwidth": 1000000, 906 | "duplex": "auto", 907 | "macaddress": "0000.0000.002f", 908 | "mode": "routed", 909 | "mtu": "1500", 910 | "speed": "auto-speed", 911 | "state": "down", 912 | "type": "Ethernet" 913 | }, 914 | "Ethernet3/43": { 915 | "bandwidth": 1000000, 916 | "duplex": "auto", 917 | "macaddress": "0000.0000.002f", 918 | "mode": "routed", 919 | "mtu": "1500", 920 | "speed": "auto-speed", 921 | "state": "down", 922 | "type": "Ethernet" 923 | }, 924 | "Ethernet3/44": { 925 | "bandwidth": 1000000, 926 | "duplex": "auto", 927 | "macaddress": "0000.0000.002f", 928 | "mode": "routed", 929 | "mtu": "1500", 930 | "speed": "auto-speed", 931 | "state": "down", 932 | "type": "Ethernet" 933 | }, 934 | "Ethernet3/45": { 935 | "bandwidth": 1000000, 936 | "duplex": "auto", 937 | "macaddress": "0000.0000.002f", 938 | "mode": "routed", 939 | "mtu": "1500", 940 | "speed": "auto-speed", 941 | "state": "down", 942 | "type": "Ethernet" 943 | }, 944 | "Ethernet3/46": { 945 | "bandwidth": 1000000, 946 | "duplex": "auto", 947 | "macaddress": "0000.0000.002f", 948 | "mode": "routed", 949 | "mtu": "1500", 950 | "speed": "auto-speed", 951 | "state": "down", 952 | "type": "Ethernet" 953 | }, 954 | "Ethernet3/47": { 955 | "bandwidth": 1000000, 956 | "duplex": "auto", 957 | "macaddress": "0000.0000.002f", 958 | "mode": "routed", 959 | "mtu": "1500", 960 | "speed": "auto-speed", 961 | "state": "down", 962 | "type": "Ethernet" 963 | }, 964 | "Ethernet3/48": { 965 | "bandwidth": 1000000, 966 | "duplex": "auto", 967 | "macaddress": "0000.0000.002f", 968 | "mode": "routed", 969 | "mtu": "1500", 970 | "speed": "auto-speed", 971 | "state": "down", 972 | "type": "Ethernet" 973 | }, 974 | "Ethernet3/5": { 975 | "bandwidth": 1000000, 976 | "duplex": "auto", 977 | "macaddress": "0000.0000.002f", 978 | "mode": "routed", 979 | "mtu": "1500", 980 | "speed": "auto-speed", 981 | "state": "down", 982 | "type": "Ethernet" 983 | }, 984 | "Ethernet3/6": { 985 | "bandwidth": 1000000, 986 | "duplex": "auto", 987 | "macaddress": "0000.0000.002f", 988 | "mode": "routed", 989 | "mtu": "1500", 990 | "speed": "auto-speed", 991 | "state": "down", 992 | "type": "Ethernet" 993 | }, 994 | "Ethernet3/7": { 995 | "bandwidth": 1000000, 996 | "duplex": "auto", 997 | "macaddress": "0000.0000.002f", 998 | "mode": "routed", 999 | "mtu": "1500", 1000 | "speed": "auto-speed", 1001 | "state": "down", 1002 | "type": "Ethernet" 1003 | }, 1004 | "Ethernet3/8": { 1005 | "bandwidth": 1000000, 1006 | "duplex": "auto", 1007 | "macaddress": "0000.0000.002f", 1008 | "mode": "routed", 1009 | "mtu": "1500", 1010 | "speed": "auto-speed", 1011 | "state": "down", 1012 | "type": "Ethernet" 1013 | }, 1014 | "Ethernet3/9": { 1015 | "bandwidth": 1000000, 1016 | "duplex": "auto", 1017 | "macaddress": "0000.0000.002f", 1018 | "mode": "routed", 1019 | "mtu": "1500", 1020 | "speed": "auto-speed", 1021 | "state": "down", 1022 | "type": "Ethernet" 1023 | }, 1024 | "Ethernet4/1": { 1025 | "bandwidth": 1000000, 1026 | "duplex": "auto", 1027 | "macaddress": "0000.0000.002f", 1028 | "mode": "routed", 1029 | "mtu": "1500", 1030 | "speed": "auto-speed", 1031 | "state": "down", 1032 | "type": "Ethernet" 1033 | }, 1034 | "Ethernet4/10": { 1035 | "bandwidth": 1000000, 1036 | "duplex": "auto", 1037 | "macaddress": "0000.0000.002f", 1038 | "mode": "routed", 1039 | "mtu": "1500", 1040 | "speed": "auto-speed", 1041 | "state": "down", 1042 | "type": "Ethernet" 1043 | }, 1044 | "Ethernet4/11": { 1045 | "bandwidth": 1000000, 1046 | "duplex": "auto", 1047 | "macaddress": "0000.0000.002f", 1048 | "mode": "routed", 1049 | "mtu": "1500", 1050 | "speed": "auto-speed", 1051 | "state": "down", 1052 | "type": "Ethernet" 1053 | }, 1054 | "Ethernet4/12": { 1055 | "bandwidth": 1000000, 1056 | "duplex": "auto", 1057 | "macaddress": "0000.0000.002f", 1058 | "mode": "routed", 1059 | "mtu": "1500", 1060 | "speed": "auto-speed", 1061 | "state": "down", 1062 | "type": "Ethernet" 1063 | }, 1064 | "Ethernet4/13": { 1065 | "bandwidth": 1000000, 1066 | "duplex": "auto", 1067 | "macaddress": "0000.0000.002f", 1068 | "mode": "routed", 1069 | "mtu": "1500", 1070 | "speed": "auto-speed", 1071 | "state": "down", 1072 | "type": "Ethernet" 1073 | }, 1074 | "Ethernet4/14": { 1075 | "bandwidth": 1000000, 1076 | "duplex": "auto", 1077 | "macaddress": "0000.0000.002f", 1078 | "mode": "routed", 1079 | "mtu": "1500", 1080 | "speed": "auto-speed", 1081 | "state": "down", 1082 | "type": "Ethernet" 1083 | }, 1084 | "Ethernet4/15": { 1085 | "bandwidth": 1000000, 1086 | "duplex": "auto", 1087 | "macaddress": "0000.0000.002f", 1088 | "mode": "routed", 1089 | "mtu": "1500", 1090 | "speed": "auto-speed", 1091 | "state": "down", 1092 | "type": "Ethernet" 1093 | }, 1094 | "Ethernet4/16": { 1095 | "bandwidth": 1000000, 1096 | "duplex": "auto", 1097 | "macaddress": "0000.0000.002f", 1098 | "mode": "routed", 1099 | "mtu": "1500", 1100 | "speed": "auto-speed", 1101 | "state": "down", 1102 | "type": "Ethernet" 1103 | }, 1104 | "Ethernet4/17": { 1105 | "bandwidth": 1000000, 1106 | "duplex": "auto", 1107 | "macaddress": "0000.0000.002f", 1108 | "mode": "routed", 1109 | "mtu": "1500", 1110 | "speed": "auto-speed", 1111 | "state": "down", 1112 | "type": "Ethernet" 1113 | }, 1114 | "Ethernet4/18": { 1115 | "bandwidth": 1000000, 1116 | "duplex": "auto", 1117 | "macaddress": "0000.0000.002f", 1118 | "mode": "routed", 1119 | "mtu": "1500", 1120 | "speed": "auto-speed", 1121 | "state": "down", 1122 | "type": "Ethernet" 1123 | }, 1124 | "Ethernet4/19": { 1125 | "bandwidth": 1000000, 1126 | "duplex": "auto", 1127 | "macaddress": "0000.0000.002f", 1128 | "mode": "routed", 1129 | "mtu": "1500", 1130 | "speed": "auto-speed", 1131 | "state": "down", 1132 | "type": "Ethernet" 1133 | }, 1134 | "Ethernet4/2": { 1135 | "bandwidth": 1000000, 1136 | "duplex": "auto", 1137 | "macaddress": "0000.0000.002f", 1138 | "mode": "routed", 1139 | "mtu": "1500", 1140 | "speed": "auto-speed", 1141 | "state": "down", 1142 | "type": "Ethernet" 1143 | }, 1144 | "Ethernet4/20": { 1145 | "bandwidth": 1000000, 1146 | "duplex": "auto", 1147 | "macaddress": "0000.0000.002f", 1148 | "mode": "routed", 1149 | "mtu": "1500", 1150 | "speed": "auto-speed", 1151 | "state": "down", 1152 | "type": "Ethernet" 1153 | }, 1154 | "Ethernet4/21": { 1155 | "bandwidth": 1000000, 1156 | "duplex": "auto", 1157 | "macaddress": "0000.0000.002f", 1158 | "mode": "routed", 1159 | "mtu": "1500", 1160 | "speed": "auto-speed", 1161 | "state": "down", 1162 | "type": "Ethernet" 1163 | }, 1164 | "Ethernet4/22": { 1165 | "bandwidth": 1000000, 1166 | "duplex": "auto", 1167 | "macaddress": "0000.0000.002f", 1168 | "mode": "routed", 1169 | "mtu": "1500", 1170 | "speed": "auto-speed", 1171 | "state": "down", 1172 | "type": "Ethernet" 1173 | }, 1174 | "Ethernet4/23": { 1175 | "bandwidth": 1000000, 1176 | "duplex": "auto", 1177 | "macaddress": "0000.0000.002f", 1178 | "mode": "routed", 1179 | "mtu": "1500", 1180 | "speed": "auto-speed", 1181 | "state": "down", 1182 | "type": "Ethernet" 1183 | }, 1184 | "Ethernet4/24": { 1185 | "bandwidth": 1000000, 1186 | "duplex": "auto", 1187 | "macaddress": "0000.0000.002f", 1188 | "mode": "routed", 1189 | "mtu": "1500", 1190 | "speed": "auto-speed", 1191 | "state": "down", 1192 | "type": "Ethernet" 1193 | }, 1194 | "Ethernet4/25": { 1195 | "bandwidth": 1000000, 1196 | "duplex": "auto", 1197 | "macaddress": "0000.0000.002f", 1198 | "mode": "routed", 1199 | "mtu": "1500", 1200 | "speed": "auto-speed", 1201 | "state": "down", 1202 | "type": "Ethernet" 1203 | }, 1204 | "Ethernet4/26": { 1205 | "bandwidth": 1000000, 1206 | "duplex": "auto", 1207 | "macaddress": "0000.0000.002f", 1208 | "mode": "routed", 1209 | "mtu": "1500", 1210 | "speed": "auto-speed", 1211 | "state": "down", 1212 | "type": "Ethernet" 1213 | }, 1214 | "Ethernet4/27": { 1215 | "bandwidth": 1000000, 1216 | "duplex": "auto", 1217 | "macaddress": "0000.0000.002f", 1218 | "mode": "routed", 1219 | "mtu": "1500", 1220 | "speed": "auto-speed", 1221 | "state": "down", 1222 | "type": "Ethernet" 1223 | }, 1224 | "Ethernet4/28": { 1225 | "bandwidth": 1000000, 1226 | "duplex": "auto", 1227 | "macaddress": "0000.0000.002f", 1228 | "mode": "routed", 1229 | "mtu": "1500", 1230 | "speed": "auto-speed", 1231 | "state": "down", 1232 | "type": "Ethernet" 1233 | }, 1234 | "Ethernet4/29": { 1235 | "bandwidth": 1000000, 1236 | "duplex": "auto", 1237 | "macaddress": "0000.0000.002f", 1238 | "mode": "routed", 1239 | "mtu": "1500", 1240 | "speed": "auto-speed", 1241 | "state": "down", 1242 | "type": "Ethernet" 1243 | }, 1244 | "Ethernet4/3": { 1245 | "bandwidth": 1000000, 1246 | "duplex": "auto", 1247 | "macaddress": "0000.0000.002f", 1248 | "mode": "routed", 1249 | "mtu": "1500", 1250 | "speed": "auto-speed", 1251 | "state": "down", 1252 | "type": "Ethernet" 1253 | }, 1254 | "Ethernet4/30": { 1255 | "bandwidth": 1000000, 1256 | "duplex": "auto", 1257 | "macaddress": "0000.0000.002f", 1258 | "mode": "routed", 1259 | "mtu": "1500", 1260 | "speed": "auto-speed", 1261 | "state": "down", 1262 | "type": "Ethernet" 1263 | }, 1264 | "Ethernet4/31": { 1265 | "bandwidth": 1000000, 1266 | "duplex": "auto", 1267 | "macaddress": "0000.0000.002f", 1268 | "mode": "routed", 1269 | "mtu": "1500", 1270 | "speed": "auto-speed", 1271 | "state": "down", 1272 | "type": "Ethernet" 1273 | }, 1274 | "Ethernet4/32": { 1275 | "bandwidth": 1000000, 1276 | "duplex": "auto", 1277 | "macaddress": "0000.0000.002f", 1278 | "mode": "routed", 1279 | "mtu": "1500", 1280 | "speed": "auto-speed", 1281 | "state": "down", 1282 | "type": "Ethernet" 1283 | }, 1284 | "Ethernet4/33": { 1285 | "bandwidth": 1000000, 1286 | "duplex": "auto", 1287 | "macaddress": "0000.0000.002f", 1288 | "mode": "routed", 1289 | "mtu": "1500", 1290 | "speed": "auto-speed", 1291 | "state": "down", 1292 | "type": "Ethernet" 1293 | }, 1294 | "Ethernet4/34": { 1295 | "bandwidth": 1000000, 1296 | "duplex": "auto", 1297 | "macaddress": "0000.0000.002f", 1298 | "mode": "routed", 1299 | "mtu": "1500", 1300 | "speed": "auto-speed", 1301 | "state": "down", 1302 | "type": "Ethernet" 1303 | }, 1304 | "Ethernet4/35": { 1305 | "bandwidth": 1000000, 1306 | "duplex": "auto", 1307 | "macaddress": "0000.0000.002f", 1308 | "mode": "routed", 1309 | "mtu": "1500", 1310 | "speed": "auto-speed", 1311 | "state": "down", 1312 | "type": "Ethernet" 1313 | }, 1314 | "Ethernet4/36": { 1315 | "bandwidth": 1000000, 1316 | "duplex": "auto", 1317 | "macaddress": "0000.0000.002f", 1318 | "mode": "routed", 1319 | "mtu": "1500", 1320 | "speed": "auto-speed", 1321 | "state": "down", 1322 | "type": "Ethernet" 1323 | }, 1324 | "Ethernet4/37": { 1325 | "bandwidth": 1000000, 1326 | "duplex": "auto", 1327 | "macaddress": "0000.0000.002f", 1328 | "mode": "routed", 1329 | "mtu": "1500", 1330 | "speed": "auto-speed", 1331 | "state": "down", 1332 | "type": "Ethernet" 1333 | }, 1334 | "Ethernet4/38": { 1335 | "bandwidth": 1000000, 1336 | "duplex": "auto", 1337 | "macaddress": "0000.0000.002f", 1338 | "mode": "routed", 1339 | "mtu": "1500", 1340 | "speed": "auto-speed", 1341 | "state": "down", 1342 | "type": "Ethernet" 1343 | }, 1344 | "Ethernet4/39": { 1345 | "bandwidth": 1000000, 1346 | "duplex": "auto", 1347 | "macaddress": "0000.0000.002f", 1348 | "mode": "routed", 1349 | "mtu": "1500", 1350 | "speed": "auto-speed", 1351 | "state": "down", 1352 | "type": "Ethernet" 1353 | }, 1354 | "Ethernet4/4": { 1355 | "bandwidth": 1000000, 1356 | "duplex": "auto", 1357 | "macaddress": "0000.0000.002f", 1358 | "mode": "routed", 1359 | "mtu": "1500", 1360 | "speed": "auto-speed", 1361 | "state": "down", 1362 | "type": "Ethernet" 1363 | }, 1364 | "Ethernet4/40": { 1365 | "bandwidth": 1000000, 1366 | "duplex": "auto", 1367 | "macaddress": "0000.0000.002f", 1368 | "mode": "routed", 1369 | "mtu": "1500", 1370 | "speed": "auto-speed", 1371 | "state": "down", 1372 | "type": "Ethernet" 1373 | }, 1374 | "Ethernet4/41": { 1375 | "bandwidth": 1000000, 1376 | "duplex": "auto", 1377 | "macaddress": "0000.0000.002f", 1378 | "mode": "routed", 1379 | "mtu": "1500", 1380 | "speed": "auto-speed", 1381 | "state": "down", 1382 | "type": "Ethernet" 1383 | }, 1384 | "Ethernet4/42": { 1385 | "bandwidth": 1000000, 1386 | "duplex": "auto", 1387 | "macaddress": "0000.0000.002f", 1388 | "mode": "routed", 1389 | "mtu": "1500", 1390 | "speed": "auto-speed", 1391 | "state": "down", 1392 | "type": "Ethernet" 1393 | }, 1394 | "Ethernet4/43": { 1395 | "bandwidth": 1000000, 1396 | "duplex": "auto", 1397 | "macaddress": "0000.0000.002f", 1398 | "mode": "routed", 1399 | "mtu": "1500", 1400 | "speed": "auto-speed", 1401 | "state": "down", 1402 | "type": "Ethernet" 1403 | }, 1404 | "Ethernet4/44": { 1405 | "bandwidth": 1000000, 1406 | "duplex": "auto", 1407 | "macaddress": "0000.0000.002f", 1408 | "mode": "routed", 1409 | "mtu": "1500", 1410 | "speed": "auto-speed", 1411 | "state": "down", 1412 | "type": "Ethernet" 1413 | }, 1414 | "Ethernet4/45": { 1415 | "bandwidth": 1000000, 1416 | "duplex": "auto", 1417 | "macaddress": "0000.0000.002f", 1418 | "mode": "routed", 1419 | "mtu": "1500", 1420 | "speed": "auto-speed", 1421 | "state": "down", 1422 | "type": "Ethernet" 1423 | }, 1424 | "Ethernet4/46": { 1425 | "bandwidth": 1000000, 1426 | "duplex": "auto", 1427 | "macaddress": "0000.0000.002f", 1428 | "mode": "routed", 1429 | "mtu": "1500", 1430 | "speed": "auto-speed", 1431 | "state": "down", 1432 | "type": "Ethernet" 1433 | }, 1434 | "Ethernet4/47": { 1435 | "bandwidth": 1000000, 1436 | "duplex": "auto", 1437 | "macaddress": "0000.0000.002f", 1438 | "mode": "routed", 1439 | "mtu": "1500", 1440 | "speed": "auto-speed", 1441 | "state": "down", 1442 | "type": "Ethernet" 1443 | }, 1444 | "Ethernet4/48": { 1445 | "bandwidth": 1000000, 1446 | "duplex": "auto", 1447 | "macaddress": "0000.0000.002f", 1448 | "mode": "routed", 1449 | "mtu": "1500", 1450 | "speed": "auto-speed", 1451 | "state": "down", 1452 | "type": "Ethernet" 1453 | }, 1454 | "Ethernet4/5": { 1455 | "bandwidth": 1000000, 1456 | "duplex": "auto", 1457 | "macaddress": "0000.0000.002f", 1458 | "mode": "routed", 1459 | "mtu": "1500", 1460 | "speed": "auto-speed", 1461 | "state": "down", 1462 | "type": "Ethernet" 1463 | }, 1464 | "Ethernet4/6": { 1465 | "bandwidth": 1000000, 1466 | "duplex": "auto", 1467 | "macaddress": "0000.0000.002f", 1468 | "mode": "routed", 1469 | "mtu": "1500", 1470 | "speed": "auto-speed", 1471 | "state": "down", 1472 | "type": "Ethernet" 1473 | }, 1474 | "Ethernet4/7": { 1475 | "bandwidth": 1000000, 1476 | "duplex": "auto", 1477 | "macaddress": "0000.0000.002f", 1478 | "mode": "routed", 1479 | "mtu": "1500", 1480 | "speed": "auto-speed", 1481 | "state": "down", 1482 | "type": "Ethernet" 1483 | }, 1484 | "Ethernet4/8": { 1485 | "bandwidth": 1000000, 1486 | "duplex": "auto", 1487 | "macaddress": "0000.0000.002f", 1488 | "mode": "routed", 1489 | "mtu": "1500", 1490 | "speed": "auto-speed", 1491 | "state": "down", 1492 | "type": "Ethernet" 1493 | }, 1494 | "Ethernet4/9": { 1495 | "bandwidth": 1000000, 1496 | "duplex": "auto", 1497 | "macaddress": "0000.0000.002f", 1498 | "mode": "routed", 1499 | "mtu": "1500", 1500 | "speed": "auto-speed", 1501 | "state": "down", 1502 | "type": "Ethernet" 1503 | }, 1504 | "mgmt0": { 1505 | "bandwidth": 1000000, 1506 | "duplex": "full", 1507 | "ipv4": { 1508 | "address": "172.31.1.101", 1509 | "masklen": 24 1510 | }, 1511 | "macaddress": "0c25.ea9d.9000", 1512 | "mode": "routed", 1513 | "mtu": "1500", 1514 | "speed": "1000 Mb/s", 1515 | "state": "up", 1516 | "type": "Ethernet" 1517 | } 1518 | }, 1519 | "net_interfaces_list": [ 1520 | "mgmt0", 1521 | "Ethernet2/1", 1522 | "Ethernet2/2", 1523 | "Ethernet2/3", 1524 | "Ethernet2/4", 1525 | "Ethernet2/5", 1526 | "Ethernet2/6", 1527 | "Ethernet2/7", 1528 | "Ethernet2/8", 1529 | "Ethernet2/9", 1530 | "Ethernet2/10", 1531 | "Ethernet2/11", 1532 | "Ethernet2/12", 1533 | "Ethernet2/13", 1534 | "Ethernet2/14", 1535 | "Ethernet2/15", 1536 | "Ethernet2/16", 1537 | "Ethernet2/17", 1538 | "Ethernet2/18", 1539 | "Ethernet2/19", 1540 | "Ethernet2/20", 1541 | "Ethernet2/21", 1542 | "Ethernet2/22", 1543 | "Ethernet2/23", 1544 | "Ethernet2/24", 1545 | "Ethernet2/25", 1546 | "Ethernet2/26", 1547 | "Ethernet2/27", 1548 | "Ethernet2/28", 1549 | "Ethernet2/29", 1550 | "Ethernet2/30", 1551 | "Ethernet2/31", 1552 | "Ethernet2/32", 1553 | "Ethernet2/33", 1554 | "Ethernet2/34", 1555 | "Ethernet2/35", 1556 | "Ethernet2/36", 1557 | "Ethernet2/37", 1558 | "Ethernet2/38", 1559 | "Ethernet2/39", 1560 | "Ethernet2/40", 1561 | "Ethernet2/41", 1562 | "Ethernet2/42", 1563 | "Ethernet2/43", 1564 | "Ethernet2/44", 1565 | "Ethernet2/45", 1566 | "Ethernet2/46", 1567 | "Ethernet2/47", 1568 | "Ethernet2/48", 1569 | "Ethernet3/1", 1570 | "Ethernet3/2", 1571 | "Ethernet3/3", 1572 | "Ethernet3/4", 1573 | "Ethernet3/5", 1574 | "Ethernet3/6", 1575 | "Ethernet3/7", 1576 | "Ethernet3/8", 1577 | "Ethernet3/9", 1578 | "Ethernet3/10", 1579 | "Ethernet3/11", 1580 | "Ethernet3/12", 1581 | "Ethernet3/13", 1582 | "Ethernet3/14", 1583 | "Ethernet3/15", 1584 | "Ethernet3/16", 1585 | "Ethernet3/17", 1586 | "Ethernet3/18", 1587 | "Ethernet3/19", 1588 | "Ethernet3/20", 1589 | "Ethernet3/21", 1590 | "Ethernet3/22", 1591 | "Ethernet3/23", 1592 | "Ethernet3/24", 1593 | "Ethernet3/25", 1594 | "Ethernet3/26", 1595 | "Ethernet3/27", 1596 | "Ethernet3/28", 1597 | "Ethernet3/29", 1598 | "Ethernet3/30", 1599 | "Ethernet3/31", 1600 | "Ethernet3/32", 1601 | "Ethernet3/33", 1602 | "Ethernet3/34", 1603 | "Ethernet3/35", 1604 | "Ethernet3/36", 1605 | "Ethernet3/37", 1606 | "Ethernet3/38", 1607 | "Ethernet3/39", 1608 | "Ethernet3/40", 1609 | "Ethernet3/41", 1610 | "Ethernet3/42", 1611 | "Ethernet3/43", 1612 | "Ethernet3/44", 1613 | "Ethernet3/45", 1614 | "Ethernet3/46", 1615 | "Ethernet3/47", 1616 | "Ethernet3/48", 1617 | "Ethernet4/1", 1618 | "Ethernet4/2", 1619 | "Ethernet4/3", 1620 | "Ethernet4/4", 1621 | "Ethernet4/5", 1622 | "Ethernet4/6", 1623 | "Ethernet4/7", 1624 | "Ethernet4/8", 1625 | "Ethernet4/9", 1626 | "Ethernet4/10", 1627 | "Ethernet4/11", 1628 | "Ethernet4/12", 1629 | "Ethernet4/13", 1630 | "Ethernet4/14", 1631 | "Ethernet4/15", 1632 | "Ethernet4/16", 1633 | "Ethernet4/17", 1634 | "Ethernet4/18", 1635 | "Ethernet4/19", 1636 | "Ethernet4/20", 1637 | "Ethernet4/21", 1638 | "Ethernet4/22", 1639 | "Ethernet4/23", 1640 | "Ethernet4/24", 1641 | "Ethernet4/25", 1642 | "Ethernet4/26", 1643 | "Ethernet4/27", 1644 | "Ethernet4/28", 1645 | "Ethernet4/29", 1646 | "Ethernet4/30", 1647 | "Ethernet4/31", 1648 | "Ethernet4/32", 1649 | "Ethernet4/33", 1650 | "Ethernet4/34", 1651 | "Ethernet4/35", 1652 | "Ethernet4/36", 1653 | "Ethernet4/37", 1654 | "Ethernet4/38", 1655 | "Ethernet4/39", 1656 | "Ethernet4/40", 1657 | "Ethernet4/41", 1658 | "Ethernet4/42", 1659 | "Ethernet4/43", 1660 | "Ethernet4/44", 1661 | "Ethernet4/45", 1662 | "Ethernet4/46", 1663 | "Ethernet4/47", 1664 | "Ethernet4/48" 1665 | ], 1666 | "net_license_hostid": "TBEA9D9000B", 1667 | "net_memfree_mb": 982.23046875, 1668 | "net_memtotal_mb": 2992.91015625, 1669 | "net_model": "NX-OSv Chassis (\"NX-OSv Supervisor Module\")", 1670 | "net_module": [ 1671 | { 1672 | "model": "N7K-SUP1", 1673 | "ports": 0, 1674 | "status": "active *", 1675 | "type": "NX-OSv Supervisor Module" 1676 | }, 1677 | { 1678 | "model": "N7K-F248XP-25", 1679 | "ports": 48, 1680 | "status": "ok", 1681 | "type": "NX-OSv Ethernet Module" 1682 | }, 1683 | { 1684 | "model": "N7K-F248XP-25", 1685 | "ports": 48, 1686 | "status": "ok", 1687 | "type": "NX-OSv Ethernet Module" 1688 | }, 1689 | { 1690 | "model": "N7K-F248XP-25", 1691 | "ports": 48, 1692 | "status": "ok", 1693 | "type": "NX-OSv Ethernet Module" 1694 | } 1695 | ], 1696 | "net_neighbors": { 1697 | "mgmt0": [ 1698 | { 1699 | "host": "rtr-1.josh-v.com", 1700 | "port": "GigabitEthernet0/1", 1701 | "sysname": "rtr-1.josh-v.com" 1702 | } 1703 | ] 1704 | }, 1705 | "net_platform": "N7K-C7018", 1706 | "net_power_supply_info": [ 1707 | { 1708 | "amps": "77.96", 1709 | "model": "DS-CAC-845W", 1710 | "number": 1, 1711 | "status": "Ok", 1712 | "watts": "3274.32" 1713 | }, 1714 | { 1715 | "amps": "0.00", 1716 | "model": "------------", 1717 | "number": 2, 1718 | "status": "Absent", 1719 | "watts": "0.00" 1720 | } 1721 | ], 1722 | "net_python_version": "3.7.7", 1723 | "net_serialnum": "TMEA9D9000B", 1724 | "net_system": "nxos", 1725 | "net_version": "7.3(0)D1(1)", 1726 | "net_vlan_list": [ 1727 | 1 1728 | ], 1729 | "network_resources": {} 1730 | } 1731 | 1732 | test_data = { 1733 | "discovered_interpreter_python": "/usr/local/bin/python3.7", 1734 | "net_all_ipv4_addresses": [ 1735 | "192.168.0.167", 1736 | "172.31.1.1", 1737 | "10.100.1.1", 1738 | "10.50.50.1" 1739 | ], 1740 | "net_all_ipv6_addresses": [], 1741 | "net_api": "cliconf", 1742 | "net_filesystems": [ 1743 | "flash0:" 1744 | ], 1745 | "net_filesystems_info": { 1746 | "flash0:": { 1747 | "spacefree_kb": 1948176.0, 1748 | "spacetotal_kb": 2092496.0 1749 | } 1750 | }, 1751 | "net_gather_network_resources": [], 1752 | "net_gather_subset": [ 1753 | "interfaces", 1754 | "default", 1755 | "hardware" 1756 | ], 1757 | "net_hostname": "rtr-1", 1758 | "net_image": "flash0:/vios-adventerprisek9-m", 1759 | "net_interfaces": { 1760 | "GigabitEthernet0/0": { 1761 | "bandwidth": 1000000, 1762 | "description": null, 1763 | "duplex": "Auto", 1764 | "ipv4": [ 1765 | { 1766 | "address": "192.168.0.167", 1767 | "subnet": "24" 1768 | } 1769 | ], 1770 | "lineprotocol": "up", 1771 | "macaddress": "0c25.ea08.4900", 1772 | "mediatype": "RJ45", 1773 | "mtu": 1500, 1774 | "operstatus": "up", 1775 | "type": "iGbE" 1776 | }, 1777 | "GigabitEthernet0/1": { 1778 | "bandwidth": 1000000, 1779 | "description": null, 1780 | "duplex": "Auto", 1781 | "ipv4": [ 1782 | { 1783 | "address": "172.31.1.1", 1784 | "subnet": "24" 1785 | } 1786 | ], 1787 | "lineprotocol": "up", 1788 | "macaddress": "0c25.ea08.4901", 1789 | "mediatype": "RJ45", 1790 | "mtu": 1500, 1791 | "operstatus": "up", 1792 | "type": "iGbE" 1793 | }, 1794 | "GigabitEthernet0/2": { 1795 | "bandwidth": 1000000, 1796 | "description": null, 1797 | "duplex": "Auto", 1798 | "ipv4": [ 1799 | { 1800 | "address": "10.100.1.1", 1801 | "subnet": "24" 1802 | } 1803 | ], 1804 | "lineprotocol": "up", 1805 | "macaddress": "0c25.ea08.4902", 1806 | "mediatype": "RJ45", 1807 | "mtu": 1500, 1808 | "operstatus": "up", 1809 | "type": "iGbE" 1810 | }, 1811 | "GigabitEthernet0/3": { 1812 | "bandwidth": 1000000, 1813 | "description": null, 1814 | "duplex": "Auto", 1815 | "ipv4": [], 1816 | "lineprotocol": "down", 1817 | "macaddress": "0c25.ea08.4903", 1818 | "mediatype": "RJ45", 1819 | "mtu": 1500, 1820 | "operstatus": "down", 1821 | "type": "iGbE" 1822 | }, 1823 | "Loopback0": { 1824 | "bandwidth": 8000000, 1825 | "description": null, 1826 | "duplex": null, 1827 | "ipv4": [ 1828 | { 1829 | "address": "10.50.50.1", 1830 | "subnet": "32" 1831 | } 1832 | ], 1833 | "lineprotocol": "up", 1834 | "macaddress": null, 1835 | "mediatype": null, 1836 | "mtu": 1514, 1837 | "operstatus": "up", 1838 | "type": null 1839 | } 1840 | }, 1841 | "net_iostype": "IOS", 1842 | "net_memfree_mb": 253007.0703125, 1843 | "net_memtotal_mb": 315455.65625, 1844 | "net_model": "IOSv", 1845 | "net_neighbors": { 1846 | "GigabitEthernet0/0": [ 1847 | { 1848 | "host": "joshv-sw01.josh-v.com", 1849 | "port": "GigabitEthernet1/0/47" 1850 | } 1851 | ], 1852 | "GigabitEthernet0/1": [ 1853 | { 1854 | "host": "nxos01(TBEA9D9000B)", 1855 | "port": "mgmt0" 1856 | }, 1857 | { 1858 | "host": "nxos03(TBEAD8A300B)", 1859 | "port": "mgmt0" 1860 | }, 1861 | { 1862 | "host": "nxos04(TBEA22BA00B)", 1863 | "port": "mgmt0" 1864 | }, 1865 | { 1866 | "host": "nxos02(TBEA761700B)", 1867 | "port": "mgmt0" 1868 | } 1869 | ], 1870 | "GigabitEthernet0/2": [ 1871 | { 1872 | "host": "rtr-4.josh-v.com", 1873 | "port": "GigabitEthernet0/0" 1874 | } 1875 | ] 1876 | }, 1877 | "net_python_version": "3.7.7", 1878 | "net_serialnum": "9GRLJCPTN0VYA3VWS0V15", 1879 | "net_system": "ios", 1880 | "net_version": "15.6(1)T", 1881 | "network_resources": {} 1882 | } 1883 | 1884 | expected_result = [ 1885 | {"interface": "GigabitEthernet0/0", "address": "192.168.0.167/24"}, 1886 | {"interface": "GigabitEthernet0/1", "address": "172.31.1.1/24"}, 1887 | {"interface": "GigabitEthernet0/2", "address": "10.100.1.1/24"}, 1888 | {"interface": "Loopback0", "address": "10.50.50.1/32"} 1889 | ] 1890 | 1891 | nxos_result = [ 1892 | {"interface": "mgmt0", "address": "172.31.1.101/24"} 1893 | ] 1894 | assert FilterModule.build_ipv4_from_facts(test_data) == expected_result 1895 | assert FilterModule.build_ipv4_from_facts(nxos_data) == nxos_result 1896 | -------------------------------------------------------------------------------- /tests/test_compare_list.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test of Ansible Filters 3 | """ 4 | from plugins.filter.filter import FilterModule 5 | 6 | 7 | def test_filtermodule_verify_compare_lists(): 8 | """ 9 | Functional test of Verify Serial Number 10 | """ 11 | device_serial1 = ["99JCD34YT4C"] 12 | netbox_serial1 = ["99JCD34YT4C"] 13 | netbox_false_serial1 = ["NTCFTW"] 14 | assert FilterModule.compare_lists(device_serial1, netbox_serial1) 15 | assert not FilterModule.compare_lists(device_serial1, netbox_false_serial1) 16 | 17 | 18 | def test_filtermodule_verify_multiple_compare_lists(): 19 | """ 20 | Functional test to verify multiple serial numbers are asserted properly 21 | """ 22 | device_serial_list1 = ["99JCD34YT4C", "548SDFHA5D"] 23 | netbox_serial1 = ["99JCD34YT4C", "548SDFHA5D"] 24 | netbox_serial2 = ["99JCD34YT4C", "548SDFHA5D", "NTCISAWESOME", "REDHATFTW"] 25 | assert FilterModule.compare_lists(device_serial_list1, netbox_serial1) 26 | assert not FilterModule.compare_lists(device_serial_list1, netbox_serial2) 27 | -------------------------------------------------------------------------------- /tests/test_get_ciscoios_serial_list.py: -------------------------------------------------------------------------------- 1 | from plugins.filter.filter import FilterModule 2 | 3 | ANSIBLE_FACTS_SINGLE = { 4 | "net_all_ipv4_addresses": ["192.168.0.164", "172.31.1.1", "172.31.0.1", "10.50.50.1",], 5 | "net_all_ipv6_addresses": [], 6 | "net_api": "cliconf", 7 | "net_filesystems": ["flash0:"], 8 | "net_filesystems_info": {"flash0:": {"spacefree_kb": 1948176.0, "spacetotal_kb": 2092496.0}}, 9 | "net_gather_network_resources": [], 10 | "net_gather_subset": ["hardware", "default", "interfaces"], 11 | "net_hostname": "BORDER", 12 | "net_image": "flash0:/vios-adventerprisek9-m", 13 | "net_interfaces": { 14 | "GigabitEthernet0/0": { 15 | "bandwidth": 1000000, 16 | "description": None, 17 | "duplex": "Auto", 18 | "ipv4": [{"address": "192.168.0.164", "subnet": "24"}], 19 | "lineprotocol": "up", 20 | "macaddress": "0c98.53d9.d800", 21 | "mediatype": "RJ45", 22 | "mtu": 1500, 23 | "operstatus": "up", 24 | "type": "iGbE", 25 | }, 26 | "GigabitEthernet0/1": { 27 | "bandwidth": 1000000, 28 | "description": None, 29 | "duplex": "Auto", 30 | "ipv4": [{"address": "172.31.1.1", "subnet": "24"}], 31 | "lineprotocol": "up", 32 | "macaddress": "0c98.53d9.d801", 33 | "mediatype": "RJ45", 34 | "mtu": 1500, 35 | "operstatus": "up", 36 | "type": "iGbE", 37 | }, 38 | "GigabitEthernet0/2": { 39 | "bandwidth": 1000000, 40 | "description": "Configured by Ansible", 41 | "duplex": "Auto", 42 | "ipv4": [{"address": "172.31.0.1", "subnet": "24"}], 43 | "lineprotocol": "up", 44 | "macaddress": "0c98.53d9.d802", 45 | "mediatype": "RJ45", 46 | "mtu": 1500, 47 | "operstatus": "up", 48 | "type": "iGbE", 49 | }, 50 | "GigabitEthernet0/3": { 51 | "bandwidth": 1000000, 52 | "description": "Configured by Ansible", 53 | "duplex": "Auto", 54 | "ipv4": [], 55 | "lineprotocol": "down", 56 | "macaddress": "0c98.53d9.d803", 57 | "mediatype": "RJ45", 58 | "mtu": 1500, 59 | "operstatus": "down", 60 | "type": "iGbE", 61 | }, 62 | "Loopback0": { 63 | "bandwidth": 8000000, 64 | "description": None, 65 | "duplex": None, 66 | "ipv4": [{"address": "10.50.50.1", "subnet": "32"}], 67 | "lineprotocol": "up", 68 | "macaddress": None, 69 | "mediatype": None, 70 | "mtu": 1514, 71 | "operstatus": "up", 72 | "type": None, 73 | }, 74 | }, 75 | "net_iostype": "IOS", 76 | "net_memfree_mb": 253431.28125, 77 | "net_memtotal_mb": 315455.65625, 78 | "net_model": "IOSv", 79 | "net_neighbors": {"GigabitEthernet0/2": [{"host": "wanrtr01.josh-v.com", "port": "Gi1"}]}, 80 | "net_python_version": "3.8.1", 81 | "net_serialnum": "98TM4IDVPLTBHYGPX4Q21", 82 | "net_system": "ios", 83 | "net_version": "15.6(1)T", 84 | "network_resources": {}, 85 | } 86 | 87 | ANSIBLE_FACTS_THREE = { 88 | "net_all_ipv4_addresses": ["192.168.0.164", "172.31.1.1", "172.31.0.1", "10.50.50.1",], 89 | "net_all_ipv6_addresses": [], 90 | "net_api": "cliconf", 91 | "net_filesystems": ["flash0:"], 92 | "net_filesystems_info": {"flash0:": {"spacefree_kb": 1948176.0, "spacetotal_kb": 2092496.0}}, 93 | "net_gather_network_resources": [], 94 | "net_gather_subset": ["hardware", "default", "interfaces"], 95 | "net_hostname": "BORDER", 96 | "net_image": "flash0:/vios-adventerprisek9-m", 97 | "net_interfaces": { 98 | "GigabitEthernet0/0": { 99 | "bandwidth": 1000000, 100 | "description": None, 101 | "duplex": "Auto", 102 | "ipv4": [{"address": "192.168.0.164", "subnet": "24"}], 103 | "lineprotocol": "up", 104 | "macaddress": "0c98.53d9.d800", 105 | "mediatype": "RJ45", 106 | "mtu": 1500, 107 | "operstatus": "up", 108 | "type": "iGbE", 109 | }, 110 | "GigabitEthernet0/1": { 111 | "bandwidth": 1000000, 112 | "description": None, 113 | "duplex": "Auto", 114 | "ipv4": [{"address": "172.31.1.1", "subnet": "24"}], 115 | "lineprotocol": "up", 116 | "macaddress": "0c98.53d9.d801", 117 | "mediatype": "RJ45", 118 | "mtu": 1500, 119 | "operstatus": "up", 120 | "type": "iGbE", 121 | }, 122 | "GigabitEthernet0/2": { 123 | "bandwidth": 1000000, 124 | "description": "Configured by Ansible", 125 | "duplex": "Auto", 126 | "ipv4": [{"address": "172.31.0.1", "subnet": "24"}], 127 | "lineprotocol": "up", 128 | "macaddress": "0c98.53d9.d802", 129 | "mediatype": "RJ45", 130 | "mtu": 1500, 131 | "operstatus": "up", 132 | "type": "iGbE", 133 | }, 134 | "GigabitEthernet0/3": { 135 | "bandwidth": 1000000, 136 | "description": "Configured by Ansible", 137 | "duplex": "Auto", 138 | "ipv4": [], 139 | "lineprotocol": "down", 140 | "macaddress": "0c98.53d9.d803", 141 | "mediatype": "RJ45", 142 | "mtu": 1500, 143 | "operstatus": "down", 144 | "type": "iGbE", 145 | }, 146 | "Loopback0": { 147 | "bandwidth": 8000000, 148 | "description": None, 149 | "duplex": None, 150 | "ipv4": [{"address": "10.50.50.1", "subnet": "32"}], 151 | "lineprotocol": "up", 152 | "macaddress": None, 153 | "mediatype": None, 154 | "mtu": 1514, 155 | "operstatus": "up", 156 | "type": None, 157 | }, 158 | }, 159 | "net_iostype": "IOS", 160 | "net_memfree_mb": 253431.28125, 161 | "net_memtotal_mb": 315455.65625, 162 | "net_model": "IOSv", 163 | "net_neighbors": {"GigabitEthernet0/2": [{"host": "wanrtr01.josh-v.com", "port": "Gi1"}]}, 164 | "net_python_version": "3.8.1", 165 | "net_stacked_serialnums": ["98TM4IDVPLTBHYGPX4Q21", "NTCMSP1", "NTCDFW2"], 166 | "net_system": "ios", 167 | "net_version": "15.6(1)T", 168 | "network_resources": {}, 169 | } 170 | 171 | 172 | def test_get_ciscoios_serial_list_single_device(): 173 | """ 174 | Functional test of get_ciscoios_serial_list 175 | """ 176 | assert FilterModule.get_ciscoios_serial_list(ANSIBLE_FACTS_SINGLE) == ["98TM4IDVPLTBHYGPX4Q21"] 177 | 178 | 179 | def test_get_ciscoios_serial_list_three_devices(): 180 | """ 181 | Functional test for multiple devices in get_ciscoios_serial_list 182 | """ 183 | assert FilterModule.get_ciscoios_serial_list(ANSIBLE_FACTS_THREE) == [ 184 | "98TM4IDVPLTBHYGPX4Q21", 185 | "NTCMSP1", 186 | "NTCDFW2", 187 | ] 188 | -------------------------------------------------------------------------------- /tests/test_get_role_from_hostname.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test of Ansible Filters 3 | """ 4 | from plugins.filter.filter import FilterModule 5 | 6 | 7 | def test_get_role_from_hostname(): 8 | """ 9 | Functional test of getting a role from the device name 10 | """ 11 | device_test_mapping = { 12 | "sw": "Switch", 13 | "rtr": "Router", 14 | "fw": "Firewall", 15 | "nxos": "Switch", 16 | "veos": "Switch" 17 | } 18 | for text_in_name, device_role in device_test_mapping.items(): 19 | assert FilterModule.get_role_from_hostname(text_in_name) == device_role # nosec 20 | 21 | device_name_mapping = { 22 | "218-sw01": "Switch", 23 | "rtr-1": "Router", 24 | "14-fw1": "Firewall", 25 | "dc01-nxos-02": "Switch", 26 | "dc04-veos-14": "Switch" 27 | } 28 | 29 | for text_in_name, device_role in device_name_mapping.items(): 30 | assert FilterModule.get_role_from_hostname(text_in_name) == device_role # nosec 31 | -------------------------------------------------------------------------------- /tests/test_mac_address.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test of Ansible Filters - MAC Address conversion 3 | """ 4 | from plugins.filter.filter import FilterModule 5 | 6 | 7 | def test_filtermodule_convert_mac_address(): 8 | """ 9 | Function to test mac address filter 10 | """ 11 | mac_address = "123a.124a.2387" 12 | mac_address2 = "12-3A-12-4A-23-87" 13 | assert FilterModule.convert_mac_address(mac_address) == "12:3A:12:4A:23:87" 14 | assert FilterModule.convert_mac_address(mac_address2) == "12:3A:12:4A:23:87" 15 | -------------------------------------------------------------------------------- /urls.md: -------------------------------------------------------------------------------- 1 | # Helpful URLs 2 | 3 | ## Network to Code 4 | 5 | https://slack.networktocode.com (Heroku App to get sign ups) 6 | https://www.networktocode.com 7 | https://blog.networktocode.com 8 | Email: info@networktocode.com 9 | 10 | ## Referenced 11 | 12 | https://github.com/netbox-community 13 | https://netbox.readthedocs.io/ 14 | --------------------------------------------------------------------------------