├── .bandit.yml ├── .cookiecutter.json ├── .dockerignore ├── .flake8 ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ ├── ci.yml │ ├── coverage.yml │ └── upstream_testing.yml ├── .gitignore ├── .pydocstyle.ini ├── .readthedocs.yaml ├── .yamllint.yml ├── LICENSE ├── README.md ├── changes ├── +nautobot-app-v2-4-1.housekeeping ├── +nautobot-app-v2-4-2.housekeeping ├── +nautobot-app-v2.5.0.housekeeping ├── .gitignore ├── 192.changed ├── 226.added ├── 232.fixed └── 245.fixed ├── development ├── Dockerfile ├── app_config_schema.py ├── creds.example.env ├── development.env ├── development_mysql.env ├── docker-compose.base.yml ├── docker-compose.dev.yml ├── docker-compose.mysql.yml ├── docker-compose.postgres.yml ├── docker-compose.redis.yml ├── nautobot_config.py └── towncrier_template.j2 ├── docs ├── admin │ ├── compatibility_matrix.md │ ├── install.md │ ├── release_notes │ │ ├── index.md │ │ ├── version_0.20.md │ │ ├── version_0.7.md │ │ ├── version_0.8.md │ │ ├── version_0.9.md │ │ ├── version_1.0.md │ │ ├── version_2.0.md │ │ ├── version_2.1.md │ │ ├── version_2.2.md │ │ └── version_2.3.md │ ├── uninstall.md │ └── upgrade.md ├── assets │ ├── extra.css │ ├── favicon.ico │ ├── nautobot_logo.png │ ├── nautobot_logo.svg │ ├── networktocode_bw.png │ └── overrides │ │ └── partials │ │ └── copyright.html ├── dev │ ├── code_reference │ │ ├── api.md │ │ ├── index.md │ │ └── package.md │ ├── contributing.md │ ├── dev_environment.md │ ├── extending.md │ ├── models.md │ └── release_checklist.md ├── images │ ├── add_asn_12345.png │ ├── add_external_peering.png │ ├── add_internal_peering.png │ ├── add_new_ri.png │ ├── autonomous_system_01.png │ ├── external_peering_created.png │ ├── icon-nautobot-bgp-models.png │ ├── internal_peering_created.png │ ├── main-page-menu.png │ ├── menu.png │ ├── peer_endpoint_01.png │ ├── peer_group_01.png │ ├── peering_01.png │ ├── peering_list.png │ └── ri_list_view.png ├── index.md ├── models │ └── autonomoussystem.md ├── requirements.txt └── user │ ├── app_getting_started.md │ ├── app_overview.md │ ├── app_use_cases.md │ ├── cisco_use_case.md │ ├── faq.md │ └── juniper_use_case.md ├── invoke.example.yml ├── invoke.mysql.yml ├── mkdocs.yml ├── nautobot_bgp_models ├── __init__.py ├── api │ ├── __init__.py │ ├── filter_backends.py │ ├── serializers.py │ ├── urls.py │ └── views.py ├── app-config-schema.json ├── choices.py ├── dolt_compat.py ├── filter_extensions.py ├── filters.py ├── forms.py ├── helpers.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_viewsets_migration.py │ ├── 0003_peergroupaddressfamily_peerendpointaddressfamily.py │ ├── 0004_use_upstream_role_part1.py │ ├── 0005_use_upstream_role_part2.py │ ├── 0006_use_upstream_role_part3.py │ ├── 0007_use_upstream_role_part4.py │ ├── 0008_nautobotv2_updates.py │ ├── 0009_autonomoussystemrange.py │ ├── 0010_alter_autonomoussystem_status_and_more.py │ └── __init__.py ├── models.py ├── navigation.py ├── signals.py ├── tables.py ├── template_content.py ├── templates │ └── nautobot_bgp_models │ │ ├── addressfamily_retrieve.html │ │ ├── autonomoussystem_retrieve.html │ │ ├── autonomoussystemrange_retrieve.html │ │ ├── bgproutinginstance_retrieve.html │ │ ├── extra_attributes.html │ │ ├── inc │ │ ├── device_address_families.html │ │ ├── device_bgp_routing_instances.html │ │ ├── device_peer_endpoints.html │ │ ├── inheritable_property.html │ │ ├── native_property.html │ │ └── peerendpoint.html │ │ ├── peerendpoint_retrieve.html │ │ ├── peerendpointaddressfamily_retrieve.html │ │ ├── peergroup_retrieve.html │ │ ├── peergroupaddressfamily_retrieve.html │ │ ├── peergrouptemplate_retrieve.html │ │ ├── peering_add.html │ │ └── peering_retrieve.html ├── tests │ ├── __init__.py │ ├── test_api.py │ ├── test_basic.py │ ├── test_filters.py │ ├── test_forms.py │ ├── test_helpers.py │ ├── test_models.py │ └── test_views.py ├── urls.py └── views.py ├── poetry.lock ├── pyproject.toml └── tasks.py /.bandit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | skips: [] 3 | # No need to check for security issues in the test scripts! 4 | exclude_dirs: 5 | - "./nautobot_bgp_models/tests/" 6 | - "./.venv/" 7 | -------------------------------------------------------------------------------- /.cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "cookiecutter": { 3 | "codeowner_github_usernames": "@glennmatthews @mzbroch", 4 | "full_name": "Network to Code, LLC", 5 | "email": "opensource@networktocode.com", 6 | "github_org": "nautobot", 7 | "app_name": "nautobot_bgp_models", 8 | "verbose_name": "BGP Models", 9 | "app_slug": "nautobot-bgp-models", 10 | "project_slug": "nautobot-app-bgp-models", 11 | "repo_url": "https://github.com/nautobot/nautobot-app-bgp-models", 12 | "base_url": "bgp", 13 | "min_nautobot_version": "2.0.3", 14 | "max_nautobot_version": "2.9999", 15 | "camel_name": "NautobotBGPModels", 16 | "project_short_description": "Nautobot BGP Models App", 17 | "model_class_name": "AutonomousSystem", 18 | "open_source_license": "Apache-2.0", 19 | "docs_base_url": "https://docs.nautobot.com", 20 | "docs_app_url": "https://docs.nautobot.com/projects/bgp-models/en/latest", 21 | "_drift_manager": { 22 | "template": "https://github.com/nautobot/cookiecutter-nautobot-app.git", 23 | "template_dir": "nautobot-app", 24 | "template_ref": "refs/tags/nautobot-app-v2.5.0", 25 | "cookie_dir": "", 26 | "branch_prefix": "drift-manager", 27 | "pull_request_strategy": "create", 28 | "post_actions": [ 29 | "ruff", 30 | "poetry" 31 | ], 32 | "draft": false, 33 | "baked_commit_ref": "d124229bea55d5d5cccbd59828f197561606ef5b" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Docker related 2 | development/Dockerfile 3 | development/docker-compose*.yml 4 | development/*.env 5 | *.env 6 | environments/ 7 | 8 | # Python 9 | **/*.pyc 10 | **/*.pyo 11 | **/__pycache__/ 12 | **/.pytest_cache/ 13 | **/.venv/ 14 | 15 | 16 | # Other 17 | docs/_build 18 | FAQ.md 19 | .git/ 20 | .gitignore 21 | .github 22 | LICENSE 23 | **/*.log 24 | **/.vscode/ 25 | invoke*.yml 26 | tasks.py 27 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = 3 | # Line length is enforced by Black, so flake8 doesn't need to check it 4 | E501, 5 | # Black disagrees with this rule, as does PEP 8; Black wins 6 | W503 7 | exclude = 8 | migrations, 9 | __pycache__, 10 | manage.py, 11 | settings.py, 12 | .venv 13 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Default owner(s) of all files in this repository 2 | * @glennmatthews @mzbroch 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: Report a reproducible bug in the current release of nautobot-bgp-models 4 | --- 5 | 6 | ### Environment 7 | * Python version: 8 | * Nautobot version: 9 | * nautobot-bgp-models version: 10 | 11 | 12 | ### Expected Behavior 13 | 14 | 15 | 16 | ### Observed Behavior 17 | 18 | 22 | ### Steps to Reproduce 23 | 1. 24 | 2. 25 | 3. 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ✨ Feature Request 3 | about: Propose a new feature or enhancement 4 | 5 | --- 6 | 7 | ### Environment 8 | * Nautobot version: 9 | * nautobot-bgp-models version: 10 | 11 | 14 | ### Proposed Functionality 15 | 16 | 21 | ### Use Case 22 | 23 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | # Closes: # 12 | 13 | ## What's Changed 14 | 15 | 23 | 24 | ## To Do 25 | 26 | 29 | - [ ] Explanation of Change(s) 30 | - [ ] Added change log fragment(s) (for more information see [the documentation](https://docs.nautobot.com/projects/core/en/stable/development/core/#creating-changelog-fragments)) 31 | - [ ] Attached Screenshots, Payload Example 32 | - [ ] Unit, Integration Tests 33 | - [ ] Documentation Updates (when adding/changing features) 34 | - [ ] Outline Remaining Work, Constraints from Design 35 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Post coverage comment" 3 | 4 | on: # yamllint disable-line rule:truthy rule:comments 5 | workflow_run: 6 | workflows: ["CI"] 7 | types: 8 | - "completed" 9 | 10 | jobs: 11 | test: 12 | name: "Post coverage comment to PR" 13 | runs-on: "ubuntu-latest" 14 | if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' # yamllint disable-line rule:quoted-strings rule:comments 15 | permissions: 16 | # Gives the action the necessary permissions for publishing new 17 | # comments in pull requests. 18 | pull-requests: "write" 19 | # Gives the action the necessary permissions for editing existing 20 | # comments (to avoid publishing multiple comments in the same PR) 21 | contents: "write" # yamllint disable-line rule:indentation rule:comments 22 | # Gives the action the necessary permissions for looking up the 23 | # workflow that launched this workflow, and download the related 24 | # artifact that contains the comment to be published 25 | actions: "read" 26 | steps: 27 | # DO NOT run actions/checkout here, for security reasons 28 | # For details, refer to https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ 29 | - name: "Post comment" 30 | uses: "py-cov-action/python-coverage-comment-action@d1ff8fbb5ff80feedb3faa0f6d7b424f417ad0e13" # v3.30 31 | with: 32 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 33 | GITHUB_PR_RUN_ID: "${{ github.event.workflow_run.id }}" 34 | # Update those if you changed the default values: 35 | # COMMENT_ARTIFACT_NAME: python-coverage-comment-action 36 | # COMMENT_FILENAME: python-coverage-comment-action.txt 37 | -------------------------------------------------------------------------------- /.github/workflows/upstream_testing.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Nautobot Upstream Monitor" 3 | 4 | on: # yamllint disable-line rule:truthy rule:comments 5 | schedule: 6 | - cron: "0 4 */2 * *" # every other day at midnight 7 | workflow_dispatch: 8 | 9 | jobs: 10 | upstream-test: 11 | uses: "nautobot/nautobot/.github/workflows/plugin_upstream_testing_base.yml@develop" 12 | with: # Below could potentially be collapsed into a single argument if a concrete relationship between both is enforced 13 | invoke_context_name: "NAUTOBOT_BGP_MODELS" 14 | plugin_name: "nautobot-bgp-models" 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ansible Retry Files 2 | *.retry 3 | 4 | # Swap files 5 | *.swp 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | wheels/ 29 | pip-wheel-metadata/ 30 | share/python-wheels/ 31 | *.egg-info/ 32 | .installed.cfg 33 | *.egg 34 | MANIFEST 35 | 36 | # PyInstaller 37 | # Usually these files are written by a python script from a template 38 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 39 | *.manifest 40 | *.spec 41 | 42 | # Installer logs 43 | pip-log.txt 44 | pip-delete-this-directory.txt 45 | 46 | # Unit test / coverage reports 47 | htmlcov/ 48 | .tox/ 49 | .nox/ 50 | .coverage 51 | .coverage.* 52 | .cache 53 | nosetests.xml 54 | coverage.xml 55 | *.cover 56 | *.py,cover 57 | .hypothesis/ 58 | .pytest_cache/ 59 | lcov.info 60 | 61 | # Translations 62 | *.mo 63 | *.pot 64 | 65 | # Django stuff: 66 | *.log 67 | local_settings.py 68 | db.sqlite3 69 | db.sqlite3-journal 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | target/ 83 | 84 | # Jupyter Notebook 85 | .ipynb_checkpoints 86 | 87 | # IPython 88 | profile_default/ 89 | ipython_config.py 90 | 91 | # pyenv 92 | .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 102 | __pypackages__/ 103 | 104 | # Celery stuff 105 | celerybeat-schedule 106 | celerybeat.pid 107 | 108 | # SageMath parsed files 109 | *.sage.py 110 | 111 | # Environments 112 | .env 113 | .venv 114 | env/ 115 | venv/ 116 | ENV/ 117 | env.bak/ 118 | venv.bak/ 119 | 120 | # Spyder project settings 121 | .spyderproject 122 | .spyproject 123 | 124 | # Rope project settings 125 | .ropeproject 126 | 127 | # mkdocs documentation 128 | /site 129 | 130 | # mypy 131 | .mypy_cache/ 132 | .dmypy.json 133 | dmypy.json 134 | 135 | # Pyre type checker 136 | .pyre/ 137 | 138 | # Editor 139 | .vscode/ 140 | 141 | ### macOS ### 142 | # General 143 | .DS_Store 144 | .AppleDouble 145 | .LSOverride 146 | 147 | # Thumbnails 148 | ._* 149 | 150 | # Files that might appear in the root of a volume 151 | .DocumentRevisions-V100 152 | .fseventsd 153 | .Spotlight-V100 154 | .TemporaryItems 155 | .Trashes 156 | .VolumeIcon.icns 157 | .com.apple.timemachine.donotpresent 158 | 159 | # Directories potentially created on remote AFP share 160 | .AppleDB 161 | .AppleDesktop 162 | Network Trash Folder 163 | Temporary Items 164 | .apdisk 165 | 166 | ### Windows ### 167 | # Windows thumbnail cache files 168 | Thumbs.db 169 | Thumbs.db:encryptable 170 | ehthumbs.db 171 | ehthumbs_vista.db 172 | 173 | # Dump file 174 | *.stackdump 175 | 176 | # Folder config file 177 | [Dd]esktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Windows Installer files 183 | *.cab 184 | *.msi 185 | *.msix 186 | *.msm 187 | *.msp 188 | 189 | # Windows shortcuts 190 | *.lnk 191 | 192 | ### PyCharm ### 193 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 194 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 195 | 196 | # User-specific stuff 197 | .idea/**/workspace.xml 198 | .idea/**/tasks.xml 199 | .idea/**/usage.statistics.xml 200 | .idea/**/dictionaries 201 | .idea/**/shelf 202 | 203 | # Generated files 204 | .idea/**/contentModel.xml 205 | 206 | # Sensitive or high-churn files 207 | .idea/**/dataSources/ 208 | .idea/**/dataSources.ids 209 | .idea/**/dataSources.local.xml 210 | .idea/**/sqlDataSources.xml 211 | .idea/**/dynamic.xml 212 | .idea/**/uiDesigner.xml 213 | .idea/**/dbnavigator.xml 214 | 215 | # Gradle 216 | .idea/**/gradle.xml 217 | .idea/**/libraries 218 | 219 | # Gradle and Maven with auto-import 220 | # When using Gradle or Maven with auto-import, you should exclude module files, 221 | # since they will be recreated, and may cause churn. Uncomment if using 222 | # auto-import. 223 | # .idea/artifacts 224 | # .idea/compiler.xml 225 | # .idea/jarRepositories.xml 226 | # .idea/modules.xml 227 | # .idea/*.iml 228 | # .idea/modules 229 | # *.iml 230 | # *.ipr 231 | 232 | # CMake 233 | cmake-build-*/ 234 | 235 | # Mongo Explorer plugin 236 | .idea/**/mongoSettings.xml 237 | 238 | # File-based project format 239 | *.iws 240 | 241 | # IntelliJ 242 | out/ 243 | 244 | # mpeltonen/sbt-idea plugin 245 | .idea_modules/ 246 | 247 | # JIRA plugin 248 | atlassian-ide-plugin.xml 249 | 250 | # Cursive Clojure plugin 251 | .idea/replstate.xml 252 | 253 | # Crashlytics plugin (for Android Studio and IntelliJ) 254 | com_crashlytics_export_strings.xml 255 | crashlytics.properties 256 | crashlytics-build.properties 257 | fabric.properties 258 | 259 | # Editor-based Rest Client 260 | .idea/httpRequests 261 | 262 | # Android studio 3.1+ serialized cache file 263 | .idea/caches/build_file_checksums.ser 264 | 265 | ### PyCharm Patch ### 266 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 267 | 268 | # *.iml 269 | # modules.xml 270 | # .idea/misc.xml 271 | # *.ipr 272 | 273 | # Sonarlint plugin 274 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 275 | .idea/**/sonarlint/ 276 | 277 | # SonarQube Plugin 278 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 279 | .idea/**/sonarIssues.xml 280 | 281 | # Markdown Navigator plugin 282 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 283 | .idea/**/markdown-navigator.xml 284 | .idea/**/markdown-navigator-enh.xml 285 | .idea/**/markdown-navigator/ 286 | 287 | # Cache file creation bug 288 | # See https://youtrack.jetbrains.com/issue/JBR-2257 289 | .idea/$CACHE_FILE$ 290 | 291 | # CodeStream plugin 292 | # https://plugins.jetbrains.com/plugin/12206-codestream 293 | .idea/codestream.xml 294 | 295 | ### vscode ### 296 | .vscode/* 297 | *.code-workspace 298 | 299 | # Rando 300 | creds.env 301 | development/*.txt 302 | 303 | # Invoke overrides 304 | invoke.yml 305 | 306 | # Docs 307 | public 308 | /compose.yaml 309 | /dump.sql 310 | /nautobot_bgp_models/static/nautobot_bgp_models/docs 311 | -------------------------------------------------------------------------------- /.pydocstyle.ini: -------------------------------------------------------------------------------- 1 | [pydocstyle] 2 | convention = google 3 | inherit = false 4 | match = (?!__init__).*\.py 5 | match-dir = (?!tests)(?!migrations)[^\.].* 6 | # D106 "Missing docstring in public nested class" complains about every inner Meta class, 7 | # and there's really no reason to require docstrings for them, so we disable it. 8 | # D212 is enabled by default in google convention, and complains if we have a docstring like: 9 | # """ 10 | # My docstring is on the line after the opening quotes instead of on the same line as them. 11 | # """ 12 | # We've discussed and concluded that we consider this to be a valid style choice. 13 | add_ignore = D106,D212 14 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # .readthedocs.yaml 3 | # Read the Docs configuration file 4 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 5 | 6 | # Required 7 | version: 2 8 | 9 | # Set the version of Python in the build environment. 10 | build: 11 | os: "ubuntu-22.04" 12 | tools: 13 | python: "3.10" 14 | 15 | mkdocs: 16 | configuration: "mkdocs.yml" 17 | fail_on_warning: true 18 | 19 | # Use our docs/requirements.txt during installation. 20 | python: 21 | install: 22 | - requirements: "docs/requirements.txt" 23 | -------------------------------------------------------------------------------- /.yamllint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: "default" 3 | rules: 4 | comments: "enable" 5 | empty-values: "disable" 6 | indentation: 7 | indent-sequences: "consistent" 8 | line-length: "disable" 9 | quoted-strings: 10 | quote-type: "double" 11 | ignore: | 12 | .venv/ 13 | compose.yaml 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache Software License 2.0 2 | 3 | Copyright (c) 2025, Network to Code, LLC 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BGP Models 2 | 3 |

4 | 5 |
6 | 7 | 8 | 9 | 10 |
11 | An App for Nautobot. 12 |

13 | 14 | ## Overview 15 | 16 | An app for [Nautobot](https://github.com/nautobot/nautobot), extending the core models with BGP-specific models. They enable modeling and management of BGP peerings, whether or not the peer device is present in Nautobot. 17 | 18 | > The initial development of this app was sponsored by Riot Games, Inc. 19 | 20 | ### Screenshots 21 | 22 | More screenshots can be found in the [Using the App](https://docs.nautobot.com/projects/bgp-models/en/latest/user/app_use_cases/) page in the documentation. Here's a quick overview of some of the app's added functionality: 23 | 24 | ![Menu](https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/develop/docs/images/main-page-menu.png) 25 | 26 | ![Autonomous System](https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/develop/docs/images/autonomous_system_01.png) 27 | 28 | ![Peering List](https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/develop/docs/images/peering_list.png) 29 | 30 | ![Peering](https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/develop/docs/images/peering_01.png) 31 | 32 | ![Peer Endpoint](https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/develop/docs/images/peer_endpoint_01.png) 33 | 34 | ![Peer Group](https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/develop/docs/images/peer_group_01.png) 35 | 36 | 37 | ## Try it out! 38 | 39 | This App is installed in the Nautobot Community Sandbox found over at [demo.nautobot.com](https://demo.nautobot.com/)! 40 | 41 | > For a full list of all the available always-on sandbox environments, head over to the main page on [networktocode.com](https://www.networktocode.com/nautobot/sandbox-environments/). 42 | 43 | ## Documentation 44 | 45 | Full documentation for this App can be found over on the [Nautobot Docs](https://docs.nautobot.com) website: 46 | 47 | - [User Guide](https://docs.nautobot.com/projects/bgp-models/en/latest/user/app_overview/) - Overview, Using the App, Getting Started. 48 | - [Administrator Guide](https://docs.nautobot.com/projects/bgp-models/en/latest/admin/install/) - How to Install, Configure, Upgrade, or Uninstall the App. 49 | - [Developer Guide](https://docs.nautobot.com/projects/bgp-models/en/latest/dev/contributing/) - Extending the App, Code Reference, Contribution Guide. 50 | - [Release Notes / Changelog](https://docs.nautobot.com/projects/bgp-models/en/latest/admin/release_notes/). 51 | - [Frequently Asked Questions](https://docs.nautobot.com/projects/bgp-models/en/latest/user/faq/). 52 | 53 | ### Contributing to the Documentation 54 | 55 | You can find all the Markdown source for the App documentation under the [`docs`](https://github.com/nautobot/nautobot-app-bgp-models/tree/develop/docs) folder in this repository. For simple edits, a Markdown capable editor is sufficient: clone the repository and edit away. 56 | 57 | If you need to view the fully-generated documentation site, you can build it with [MkDocs](https://www.mkdocs.org/). A container hosting the documentation can be started using the `invoke` commands (details in the [Development Environment Guide](https://docs.nautobot.com/projects/bgp-models/en/latest/dev/dev_environment/#docker-development-environment)) on [http://localhost:8001](http://localhost:8001). Using this container, as your changes to the documentation are saved, they will be automatically rebuilt and any pages currently being viewed will be reloaded in your browser. 58 | 59 | Any PRs with fixes or improvements are very welcome! 60 | 61 | ## Questions 62 | 63 | For any questions or comments, please check the [FAQ](https://docs.nautobot.com/projects/bgp-models/en/latest/user/faq/) first. Feel free to also swing by the [Network to Code Slack](https://networktocode.slack.com/) (channel `#nautobot`), sign up [here](http://slack.networktocode.com/) if you don't have an account. 64 | -------------------------------------------------------------------------------- /changes/+nautobot-app-v2-4-1.housekeeping: -------------------------------------------------------------------------------- 1 | Rebaked from the cookie `nautobot-app-v2.4.1`. 2 | -------------------------------------------------------------------------------- /changes/+nautobot-app-v2-4-2.housekeeping: -------------------------------------------------------------------------------- 1 | Rebaked from the cookie `nautobot-app-v2.4.2`. 2 | -------------------------------------------------------------------------------- /changes/+nautobot-app-v2.5.0.housekeeping: -------------------------------------------------------------------------------- 1 | Rebaked from the cookie `nautobot-app-v2.5.0`. 2 | -------------------------------------------------------------------------------- /changes/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | -------------------------------------------------------------------------------- /changes/192.changed: -------------------------------------------------------------------------------- 1 | Changed all `q` search methods to use `SearchFilter`. -------------------------------------------------------------------------------- /changes/226.added: -------------------------------------------------------------------------------- 1 | Added `q` search filters to `AddressFamilyFilterSet` and `PeeringFilterSet`. -------------------------------------------------------------------------------- /changes/232.fixed: -------------------------------------------------------------------------------- 1 | Fixed PeerEndpoint validation to allow all interfaces (including Virtual Chassis) -------------------------------------------------------------------------------- /changes/245.fixed: -------------------------------------------------------------------------------- 1 | Fixed device URL link in BGP Routing Instance detail view. -------------------------------------------------------------------------------- /development/Dockerfile: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------------- 2 | # Nautobot App Developement Dockerfile Template 3 | # Version: 1.1.0 4 | # 5 | # Apps that need to add additional steps or packages can do in the section below. 6 | # ------------------------------------------------------------------------------------- 7 | # !!! USE CAUTION WHEN MODIFYING LINES BELOW 8 | 9 | # Accepts a desired Nautobot version as build argument, default to 2.0.3 10 | ARG NAUTOBOT_VER="2.0.3" 11 | 12 | # Accepts a desired Python version as build argument, default to 3.11 13 | ARG PYTHON_VER="3.11" 14 | 15 | # Retrieve published development image of Nautobot base which should include most CI dependencies 16 | FROM ghcr.io/nautobot/nautobot-dev:${NAUTOBOT_VER}-py${PYTHON_VER} 17 | 18 | # Runtime argument and environment setup 19 | ARG NAUTOBOT_ROOT=/opt/nautobot 20 | 21 | ENV prometheus_multiproc_dir=/prom_cache 22 | ENV NAUTOBOT_ROOT=${NAUTOBOT_ROOT} 23 | ENV INVOKE_NAUTOBOT_BGP_MODELS_LOCAL=true 24 | 25 | # Install Poetry manually via its installer script; 26 | # We might be using an older version of Nautobot that includes an older version of Poetry 27 | # and CI and local development may have a newer version of Poetry 28 | # Since this is only used for development and we don't ship this container, pinning Poetry back is not expressly necessary 29 | # We also don't need virtual environments in container 30 | RUN which poetry || curl -sSL https://install.python-poetry.org | python3 - && \ 31 | poetry config virtualenvs.create false 32 | 33 | # !!! USE CAUTION WHEN MODIFYING LINES ABOVE 34 | # ------------------------------------------------------------------------------------- 35 | # App-specifc system build/test dependencies. 36 | # 37 | # Example: LDAP requires `libldap2-dev` to be apt-installed before the Python package. 38 | # ------------------------------------------------------------------------------------- 39 | # --> Start safe to modify section 40 | 41 | # Uncomment the lines below if you are apt-installing any package. 42 | # RUN apt-get -y update && apt-get -y install \ 43 | # libldap2-dev \ 44 | # && rm -rf /var/lib/apt/lists/* 45 | 46 | # --> Stop safe to modify section 47 | # ------------------------------------------------------------------------------------- 48 | # Install Nautobot App 49 | # ------------------------------------------------------------------------------------- 50 | # !!! USE CAUTION WHEN MODIFYING LINES BELOW 51 | 52 | # Copy in the source code 53 | WORKDIR /source 54 | COPY . /source 55 | 56 | # Build args must be declared in each stage 57 | ARG NAUTOBOT_VER 58 | ARG PYTHON_VER 59 | 60 | # Constrain the Nautobot version to NAUTOBOT_VER, fall back to installing from git branch if not available on PyPi 61 | # In CI, this should be done outside of the Dockerfile to prevent cross-compile build failures 62 | ARG CI 63 | RUN if [ -z "${CI+x}" ]; then \ 64 | INSTALLED_NAUTOBOT_VER=$(pip show nautobot | grep "^Version" | sed "s/Version: //"); \ 65 | poetry add --lock nautobot@${INSTALLED_NAUTOBOT_VER} --python ${PYTHON_VER} || \ 66 | poetry add --lock git+https://github.com/nautobot/nautobot.git#${NAUTOBOT_VER} --python ${PYTHON_VER}; fi 67 | 68 | # Install the app 69 | RUN poetry install --extras all --with dev 70 | 71 | COPY development/nautobot_config.py ${NAUTOBOT_ROOT}/nautobot_config.py 72 | # !!! USE CAUTION WHEN MODIFYING LINES ABOVE 73 | -------------------------------------------------------------------------------- /development/app_config_schema.py: -------------------------------------------------------------------------------- 1 | """App Config Schema Generator and Validator.""" 2 | 3 | import json 4 | from importlib import import_module 5 | from os import getenv 6 | from pathlib import Path 7 | from urllib.parse import urlparse 8 | 9 | import jsonschema 10 | import toml 11 | from django.conf import settings 12 | from to_json_schema.to_json_schema import SchemaBuilder 13 | 14 | 15 | def _enrich_object_schema(schema, defaults, required): 16 | schema["additionalProperties"] = False 17 | for key, value in schema["properties"].items(): 18 | if required and key in required: 19 | value["required"] = True 20 | default_value = defaults and defaults.get(key, None) 21 | if value["type"] == "object" and "properties" in value: 22 | _enrich_object_schema(value, default_value, None) 23 | elif default_value is not None: 24 | value["default"] = default_value 25 | 26 | 27 | def _main(): 28 | pyproject = toml.loads(Path("pyproject.toml").read_text()) 29 | url = urlparse(pyproject["tool"]["poetry"]["repository"]) 30 | _, owner, repository = url.path.split("/") 31 | package_name = pyproject["tool"]["poetry"]["packages"][0]["include"] 32 | app_config = settings.PLUGINS_CONFIG[package_name] # type: ignore 33 | schema_path = Path(package_name) / "app-config-schema.json" 34 | command = getenv("APP_CONFIG_SCHEMA_COMMAND", "") 35 | if command == "generate": 36 | schema = { 37 | "$schema": "https://json-schema.org/draft/2020-12/schema", 38 | "$id": f"https://raw.githubusercontent.com/{owner}/{repository}/develop/{package_name}/app-config-schema.json", 39 | "$comment": "TBD: Update $id, replace `develop` with the future release tag", 40 | **SchemaBuilder().to_json_schema(app_config), # type: ignore 41 | } 42 | app_config = import_module(package_name).config 43 | _enrich_object_schema(schema, app_config.default_settings, app_config.required_settings) 44 | schema_path.write_text(json.dumps(schema, indent=4) + "\n") 45 | print(f"\n==================\nGenerated schema:\n\n{schema_path}\n") 46 | print( 47 | "WARNING: Review and edit the generated file before committing.\n" 48 | "\n" 49 | "Its content is inferred from:\n" 50 | "\n" 51 | "- The current configuration in `PLUGINS_CONFIG`\n" 52 | "- `NautobotAppConfig.default_settings`\n" 53 | "- `NautobotAppConfig.required_settings`" 54 | ) 55 | elif command == "validate": 56 | schema = json.loads(schema_path.read_text()) 57 | jsonschema.validate(app_config, schema) 58 | print( 59 | f"\n==================\nValidated configuration using the schema:\n{schema_path}\nConfiguration is valid." 60 | ) 61 | else: 62 | raise RuntimeError(f"Unknown command: {command}") 63 | 64 | 65 | _main() 66 | -------------------------------------------------------------------------------- /development/creds.example.env: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # CREDS File: Store private information. Copied to creds.env and always ignored 3 | ################################################################################ 4 | # Nautobot Configuration Secret Items 5 | NAUTOBOT_CREATE_SUPERUSER=true 6 | NAUTOBOT_DB_PASSWORD=changeme 7 | NAUTOBOT_NAPALM_USERNAME='' 8 | NAUTOBOT_NAPALM_PASSWORD='' 9 | NAUTOBOT_REDIS_PASSWORD=changeme 10 | NAUTOBOT_SECRET_KEY='changeme' 11 | NAUTOBOT_SUPERUSER_NAME=admin 12 | NAUTOBOT_SUPERUSER_EMAIL=admin@example.com 13 | NAUTOBOT_SUPERUSER_PASSWORD=admin 14 | NAUTOBOT_SUPERUSER_API_TOKEN=0123456789abcdef0123456789abcdef01234567 15 | 16 | # Postgres 17 | POSTGRES_PASSWORD=${NAUTOBOT_DB_PASSWORD} 18 | PGPASSWORD=${NAUTOBOT_DB_PASSWORD} 19 | 20 | # MySQL Credentials 21 | MYSQL_ROOT_PASSWORD=${NAUTOBOT_DB_PASSWORD} 22 | MYSQL_PASSWORD=${NAUTOBOT_DB_PASSWORD} 23 | 24 | # Use these to override values in development.env 25 | # NAUTOBOT_DB_HOST=localhost 26 | # NAUTOBOT_REDIS_HOST=localhost 27 | # NAUTOBOT_CONFIG=development/nautobot_config.py 28 | -------------------------------------------------------------------------------- /development/development.env: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # DEV File: Store environment information. NOTE: Secrets NOT stored here! 3 | ################################################################################ 4 | # Nautobot Configuration Environment Variables 5 | NAUTOBOT_ALLOWED_HOSTS=* 6 | NAUTOBOT_BANNER_TOP="Local" 7 | NAUTOBOT_CHANGELOG_RETENTION=0 8 | 9 | NAUTOBOT_DEBUG=True 10 | NAUTOBOT_LOG_DEPRECATION_WARNINGS=True 11 | NAUTOBOT_LOG_LEVEL=DEBUG 12 | NAUTOBOT_METRICS_ENABLED=True 13 | NAUTOBOT_NAPALM_TIMEOUT=5 14 | NAUTOBOT_MAX_PAGE_SIZE=0 15 | 16 | # Redis Configuration Environment Variables 17 | NAUTOBOT_REDIS_HOST=redis 18 | NAUTOBOT_REDIS_PORT=6379 19 | # Uncomment NAUTOBOT_REDIS_SSL if using SSL 20 | # NAUTOBOT_REDIS_SSL=True 21 | 22 | # Nautobot DB Connection Environment Variables 23 | NAUTOBOT_DB_NAME=nautobot 24 | NAUTOBOT_DB_USER=nautobot 25 | NAUTOBOT_DB_HOST=db 26 | NAUTOBOT_DB_TIMEOUT=300 27 | 28 | # Use them to overwrite the defaults in nautobot_config.py 29 | # NAUTOBOT_DB_ENGINE=django.db.backends.postgresql 30 | # NAUTOBOT_DB_PORT=5432 31 | 32 | # Needed for Postgres should match the values for Nautobot above 33 | POSTGRES_USER=${NAUTOBOT_DB_USER} 34 | POSTGRES_DB=${NAUTOBOT_DB_NAME} 35 | 36 | # Needed for MYSQL should match the values for Nautobot above 37 | MYSQL_USER=${NAUTOBOT_DB_USER} 38 | MYSQL_DATABASE=${NAUTOBOT_DB_NAME} 39 | MYSQL_ROOT_HOST=% 40 | 41 | # Use a less verbose log level for Celery Beat 42 | NAUTOBOT_BEAT_LOG_LEVEL=INFO 43 | -------------------------------------------------------------------------------- /development/development_mysql.env: -------------------------------------------------------------------------------- 1 | # Custom ENVs for Mysql 2 | # Due to docker image limitations for Mysql, we need "root" user to create more than one database table 3 | NAUTOBOT_DB_USER=root 4 | -------------------------------------------------------------------------------- /development/docker-compose.base.yml: -------------------------------------------------------------------------------- 1 | --- 2 | x-nautobot-build: &nautobot-build 3 | build: 4 | args: 5 | NAUTOBOT_VER: "${NAUTOBOT_VER}" 6 | PYTHON_VER: "${PYTHON_VER}" 7 | context: "../" 8 | dockerfile: "development/Dockerfile" 9 | x-nautobot-base: &nautobot-base 10 | image: "nautobot-bgp-models/nautobot:${NAUTOBOT_VER}-py${PYTHON_VER}" 11 | env_file: 12 | - "development.env" 13 | - "creds.env" 14 | tty: true 15 | 16 | services: 17 | nautobot: 18 | depends_on: 19 | redis: 20 | condition: "service_started" 21 | db: 22 | condition: "service_healthy" 23 | <<: 24 | - *nautobot-base 25 | - *nautobot-build 26 | worker: 27 | entrypoint: 28 | - "sh" 29 | - "-c" # this is to evaluate the $NAUTOBOT_LOG_LEVEL from the env 30 | - "nautobot-server celery worker -l $$NAUTOBOT_LOG_LEVEL --events" ## $$ because of docker-compose 31 | depends_on: 32 | nautobot: 33 | condition: "service_healthy" 34 | healthcheck: 35 | interval: "30s" 36 | timeout: "10s" 37 | start_period: "30s" 38 | retries: 3 39 | test: ["CMD", "bash", "-c", "nautobot-server celery inspect ping --destination celery@$$HOSTNAME"] ## $$ because of docker-compose 40 | <<: *nautobot-base 41 | beat: 42 | entrypoint: 43 | - "sh" 44 | - "-c" # this is to evaluate the $NAUTOBOT_BEAT_LOG_LEVEL from the env 45 | - "nautobot-server celery beat -l $$NAUTOBOT_BEAT_LOG_LEVEL" ## $$ because of docker-compose 46 | depends_on: 47 | nautobot: 48 | condition: "service_healthy" 49 | healthcheck: 50 | disable: true 51 | <<: *nautobot-base 52 | -------------------------------------------------------------------------------- /development/docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | # We can't remove volumes in a compose override, for the test configuration using the final containers 2 | # we don't want the volumes so this is the default override file to add the volumes in the dev case 3 | # any override will need to include these volumes to use them. 4 | # see: https://github.com/docker/compose/issues/3729 5 | --- 6 | services: 7 | nautobot: 8 | command: "nautobot-server runserver 0.0.0.0:8080" 9 | ports: 10 | - "8080:8080" 11 | volumes: 12 | - "./nautobot_config.py:/opt/nautobot/nautobot_config.py" 13 | - "../:/source" 14 | healthcheck: 15 | interval: "30s" 16 | timeout: "10s" 17 | start_period: "60s" 18 | retries: 3 19 | test: ["CMD", "true"] # Due to layering, disable: true won't work. Instead, change the test 20 | docs: 21 | entrypoint: "mkdocs serve -v -a 0.0.0.0:8080" 22 | ports: 23 | - "8001:8080" 24 | volumes: 25 | - "../:/source" 26 | image: "nautobot-bgp-models/nautobot:${NAUTOBOT_VER}-py${PYTHON_VER}" 27 | healthcheck: 28 | disable: true 29 | tty: true 30 | worker: 31 | entrypoint: 32 | - "sh" 33 | - "-c" # this is to evaluate the $NAUTOBOT_LOG_LEVEL from the env 34 | - "watchmedo auto-restart --directory './' --pattern '*.py' --recursive -- nautobot-server celery worker -l $$NAUTOBOT_LOG_LEVEL --events" ## $$ because of docker-compose 35 | volumes: 36 | - "./nautobot_config.py:/opt/nautobot/nautobot_config.py" 37 | - "../:/source" 38 | healthcheck: 39 | test: ["CMD", "true"] # Due to layering, disable: true won't work. Instead, change the test 40 | beat: 41 | entrypoint: 42 | - "sh" 43 | - "-c" # this is to evaluate the $NAUTOBOT_BEAT_LOG_LEVEL from the env 44 | - "watchmedo auto-restart --directory './' --pattern '*.py' --recursive -- nautobot-server celery beat -l $$NAUTOBOT_BEAT_LOG_LEVEL" ## $$ because of docker-compose 45 | volumes: 46 | - "./nautobot_config.py:/opt/nautobot/nautobot_config.py" 47 | - "../:/source" 48 | # To expose postgres (5432), myql (3306) on db service or redis (6379) to the host uncomment the 49 | # following. Ensure to match the 2 idented spaces which to have the service nested under services. 50 | # db: 51 | # ports: 52 | # - "5432:5432" 53 | # - "3306:3306" 54 | # redis: 55 | # ports: 56 | # - "6379:6379" 57 | -------------------------------------------------------------------------------- /development/docker-compose.mysql.yml: -------------------------------------------------------------------------------- 1 | --- 2 | services: 3 | nautobot: 4 | environment: 5 | - "NAUTOBOT_DB_ENGINE=django.db.backends.mysql" 6 | env_file: 7 | - "development.env" 8 | - "creds.env" 9 | - "development_mysql.env" 10 | worker: 11 | environment: 12 | - "NAUTOBOT_DB_ENGINE=django.db.backends.mysql" 13 | env_file: 14 | - "development.env" 15 | - "creds.env" 16 | - "development_mysql.env" 17 | beat: 18 | environment: 19 | - "NAUTOBOT_DB_ENGINE=django.db.backends.mysql" 20 | env_file: 21 | - "development.env" 22 | - "creds.env" 23 | - "development_mysql.env" 24 | db: 25 | image: "mysql:lts" 26 | command: 27 | - "--max_connections=1000" 28 | env_file: 29 | - "development.env" 30 | - "creds.env" 31 | - "development_mysql.env" 32 | volumes: 33 | - "mysql_data:/var/lib/mysql" 34 | healthcheck: 35 | test: 36 | - "CMD" 37 | - "mysqladmin" 38 | - "ping" 39 | - "-h" 40 | - "localhost" 41 | timeout: "20s" 42 | retries: 10 43 | volumes: 44 | mysql_data: {} 45 | -------------------------------------------------------------------------------- /development/docker-compose.postgres.yml: -------------------------------------------------------------------------------- 1 | --- 2 | services: 3 | nautobot: 4 | environment: 5 | - "NAUTOBOT_DB_ENGINE=django.db.backends.postgresql" 6 | db: 7 | image: "postgres:17-alpine" 8 | command: 9 | - "-c" 10 | - "max_connections=200" 11 | env_file: 12 | - "development.env" 13 | - "creds.env" 14 | volumes: 15 | - "postgres_data:/var/lib/postgresql/data" 16 | healthcheck: 17 | test: "pg_isready --username=$$POSTGRES_USER --dbname=$$POSTGRES_DB" 18 | interval: "10s" 19 | timeout: "5s" 20 | retries: 10 21 | 22 | volumes: 23 | postgres_data: {} 24 | -------------------------------------------------------------------------------- /development/docker-compose.redis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | services: 3 | redis: 4 | image: "redis:6-alpine" 5 | command: 6 | - "sh" 7 | - "-c" # this is to evaluate the $NAUTOBOT_REDIS_PASSWORD from the env 8 | - "redis-server --appendonly yes --requirepass $$NAUTOBOT_REDIS_PASSWORD" 9 | env_file: 10 | - "development.env" 11 | - "creds.env" 12 | -------------------------------------------------------------------------------- /development/nautobot_config.py: -------------------------------------------------------------------------------- 1 | """Nautobot development configuration file.""" 2 | 3 | import os 4 | import sys 5 | 6 | from nautobot.core.settings import * # noqa: F403 # pylint: disable=wildcard-import,unused-wildcard-import 7 | from nautobot.core.settings_funcs import is_truthy 8 | 9 | # 10 | # Debug 11 | # 12 | 13 | DEBUG = is_truthy(os.getenv("NAUTOBOT_DEBUG", "false")) 14 | _TESTING = len(sys.argv) > 1 and sys.argv[1] == "test" 15 | 16 | if DEBUG and not _TESTING: 17 | DEBUG_TOOLBAR_CONFIG = {"SHOW_TOOLBAR_CALLBACK": lambda _request: True} 18 | 19 | if "debug_toolbar" not in INSTALLED_APPS: # noqa: F405 20 | INSTALLED_APPS.append("debug_toolbar") # noqa: F405 21 | if "debug_toolbar.middleware.DebugToolbarMiddleware" not in MIDDLEWARE: # noqa: F405 22 | MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware") # noqa: F405 23 | 24 | # 25 | # Misc. settings 26 | # 27 | 28 | ALLOWED_HOSTS = os.getenv("NAUTOBOT_ALLOWED_HOSTS", "").split(" ") 29 | SECRET_KEY = os.getenv("NAUTOBOT_SECRET_KEY", "") 30 | 31 | # 32 | # Database 33 | # 34 | 35 | nautobot_db_engine = os.getenv("NAUTOBOT_DB_ENGINE", "django.db.backends.postgresql") 36 | default_db_settings = { 37 | "django.db.backends.postgresql": { 38 | "NAUTOBOT_DB_PORT": "5432", 39 | }, 40 | "django.db.backends.mysql": { 41 | "NAUTOBOT_DB_PORT": "3306", 42 | }, 43 | } 44 | DATABASES = { 45 | "default": { 46 | "NAME": os.getenv("NAUTOBOT_DB_NAME", "nautobot"), # Database name 47 | "USER": os.getenv("NAUTOBOT_DB_USER", ""), # Database username 48 | "PASSWORD": os.getenv("NAUTOBOT_DB_PASSWORD", ""), # Database password 49 | "HOST": os.getenv("NAUTOBOT_DB_HOST", "localhost"), # Database server 50 | "PORT": os.getenv( 51 | "NAUTOBOT_DB_PORT", 52 | default_db_settings[nautobot_db_engine]["NAUTOBOT_DB_PORT"], 53 | ), # Database port, default to postgres 54 | "CONN_MAX_AGE": int(os.getenv("NAUTOBOT_DB_TIMEOUT", "300")), # Database timeout 55 | "ENGINE": nautobot_db_engine, 56 | } 57 | } 58 | 59 | # Ensure proper Unicode handling for MySQL 60 | if DATABASES["default"]["ENGINE"] == "django.db.backends.mysql": 61 | DATABASES["default"]["OPTIONS"] = {"charset": "utf8mb4"} 62 | 63 | # 64 | # Redis 65 | # 66 | 67 | # The django-redis cache is used to establish concurrent locks using Redis. 68 | # Inherited from nautobot.core.settings 69 | # CACHES = {....} 70 | 71 | # 72 | # Celery settings are not defined here because they can be overloaded with 73 | # environment variables. By default they use `CACHES["default"]["LOCATION"]`. 74 | # 75 | 76 | # 77 | # Logging 78 | # 79 | 80 | LOG_LEVEL = "DEBUG" if DEBUG else "INFO" 81 | 82 | # Verbose logging during normal development operation, but quiet logging during unit test execution 83 | if not _TESTING: 84 | LOGGING = { 85 | "version": 1, 86 | "disable_existing_loggers": False, 87 | "formatters": { 88 | "normal": { 89 | "format": "%(asctime)s.%(msecs)03d %(levelname)-7s %(name)s : %(message)s", 90 | "datefmt": "%H:%M:%S", 91 | }, 92 | "verbose": { 93 | "format": "%(asctime)s.%(msecs)03d %(levelname)-7s %(name)-20s %(filename)-15s %(funcName)30s() : %(message)s", 94 | "datefmt": "%H:%M:%S", 95 | }, 96 | }, 97 | "handlers": { 98 | "normal_console": { 99 | "level": "INFO", 100 | "class": "logging.StreamHandler", 101 | "formatter": "normal", 102 | }, 103 | "verbose_console": { 104 | "level": "DEBUG", 105 | "class": "logging.StreamHandler", 106 | "formatter": "verbose", 107 | }, 108 | }, 109 | "loggers": { 110 | "django": {"handlers": ["normal_console"], "level": "INFO"}, 111 | "nautobot": { 112 | "handlers": ["verbose_console" if DEBUG else "normal_console"], 113 | "level": LOG_LEVEL, 114 | }, 115 | }, 116 | } 117 | 118 | # 119 | # Apps 120 | # 121 | 122 | # Enable installed Apps. Add the name of each App to the list. 123 | PLUGINS = ["nautobot_bgp_models"] 124 | 125 | # Apps configuration settings. These settings are used by various Apps that the user may have installed. 126 | # Each key in the dictionary is the name of an installed App and its value is a dictionary of settings. 127 | # PLUGINS_CONFIG = { 128 | # 'nautobot_bgp_models': { 129 | # 'foo': 'bar', 130 | # 'buzz': 'bazz' 131 | # } 132 | # } 133 | -------------------------------------------------------------------------------- /development/towncrier_template.j2: -------------------------------------------------------------------------------- 1 | 2 | # v{{ versiondata.version.split(".")[:2] | join(".") }} Release Notes 3 | 4 | This document describes all new features and changes in the release. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 5 | 6 | ## Release Overview 7 | 8 | - Major features or milestones 9 | - Changes to compatibility with Nautobot and/or other apps, libraries etc. 10 | 11 | {% if render_title %} 12 | ## [v{{ versiondata.version }} ({{ versiondata.date }})](https://github.com/nautobot/nautobot-app-bgp-models/releases/tag/v{{ versiondata.version}}) 13 | 14 | {% endif %} 15 | {% for section, _ in sections.items() %} 16 | {% if sections[section] %} 17 | {% for category, val in definitions.items() if category in sections[section] %} 18 | {% if sections[section][category]|length != 0 %} 19 | ### {{ definitions[category]['name'] }} 20 | 21 | {% if definitions[category]['showcontent'] %} 22 | {% for text, values in sections[section][category].items() %} 23 | {% for item in text.split('\n') %} 24 | {% if values %} 25 | - {{ values|join(', ') }} - {{ item.strip() }} 26 | {% else %} 27 | - {{ item.strip() }} 28 | {% endif %} 29 | {% endfor %} 30 | {% endfor %} 31 | 32 | {% else %} 33 | - {{ sections[section][category]['']|join(', ') }} 34 | 35 | {% endif %} 36 | {% endif %} 37 | {% endfor %} 38 | {% else %} 39 | No significant changes. 40 | 41 | {% endif %} 42 | {% endfor %} 43 | 44 | -------------------------------------------------------------------------------- /docs/admin/compatibility_matrix.md: -------------------------------------------------------------------------------- 1 | # Compatibility Matrix 2 | 3 | | BGP Models Version | Nautobot First Support Version | Nautobot Last Support Version | 4 | |--------------------|--------------------------------|-------------------------------| 5 | | 0.7.0 | 1.3.0 | 1.99.99 | 6 | | 0.7.1 | 1.5.4 | 1.99.99 | 7 | | 0.8.0 | 1.5.4 | 1.99.99 | 8 | | 0.9.0 | 1.5.4 | 1.99.99 | 9 | | 0.9.1 | 1.5.4 | 1.99.99 | 10 | | 1.0.0 | 1.5.4 | 1.99.99 | 11 | | 0.20.0 | 2.0.0 | 2.99.99 | 12 | | 0.20.1 | 2.0.3 | 2.99.99 | 13 | | 2.0.0 | 2.0.3 | 2.2.99 | 14 | | 2.1.0 | 2.0.3 | 2.2.99 | 15 | | 2.2.0 | 2.0.3 | 2.99.99 | 16 | | 2.3.0 | 2.0.3 | 2.99.99 | 17 | -------------------------------------------------------------------------------- /docs/admin/install.md: -------------------------------------------------------------------------------- 1 | # Installing the App in Nautobot 2 | 3 | Here you will find detailed instructions on how to **install** and **configure** the App within your Nautobot environment. 4 | 5 | ## Prerequisites 6 | 7 | - The app is compatible with Nautobot 2.0.3 and higher. 8 | - Databases supported: PostgreSQL, MySQL 9 | 10 | !!! note 11 | Please check the [dedicated page](compatibility_matrix.md) for a full compatibility matrix and the deprecation policy. 12 | 13 | ## Install Guide 14 | 15 | !!! note 16 | Apps can be installed from the [Python Package Index](https://pypi.org/) or locally. See the [Nautobot documentation](https://docs.nautobot.com/projects/core/en/stable/user-guide/administration/installation/app-install/) for more details. The pip package name for this app is [`nautobot-bgp-models`](https://pypi.org/project/nautobot-bgp-models/). 17 | 18 | The app is available as a Python package via PyPI and can be installed with `pip`: 19 | 20 | ```shell 21 | pip install nautobot-bgp-models 22 | ``` 23 | 24 | To ensure BGP Models is automatically re-installed during future upgrades, create a file named `local_requirements.txt` (if not already existing) in the Nautobot root directory (alongside `requirements.txt`) and list the `nautobot-bgp-models` package: 25 | 26 | ```shell 27 | echo nautobot-bgp-models >> local_requirements.txt 28 | ``` 29 | 30 | Once installed, the app needs to be enabled in your Nautobot configuration. The following block of code below shows the additional configuration required to be added to your `nautobot_config.py` file: 31 | 32 | - Append `"nautobot_bgp_models"` to the `PLUGINS` list. 33 | - Append the `"nautobot_bgp_models"` dictionary to the `PLUGINS_CONFIG` dictionary and override any defaults. 34 | 35 | ```python 36 | # In your nautobot_config.py 37 | PLUGINS = ["nautobot_bgp_models"] 38 | 39 | # PLUGINS_CONFIG = { 40 | # "nautobot_bgp_models": { 41 | # ADD YOUR SETTINGS HERE 42 | # } 43 | # } 44 | ``` 45 | 46 | Once the Nautobot configuration is updated, run the Post Upgrade command (`nautobot-server post_upgrade`) to run migrations and clear any cache: 47 | 48 | ```shell 49 | nautobot-server post_upgrade 50 | ``` 51 | 52 | Then restart (if necessary) the Nautobot services which may include: 53 | 54 | - Nautobot 55 | - Nautobot Workers 56 | - Nautobot Scheduler 57 | 58 | ```shell 59 | sudo systemctl restart nautobot nautobot-worker nautobot-scheduler 60 | ``` 61 | 62 | ## App Configuration 63 | 64 | An example configuration is provided below: 65 | 66 | ```python 67 | PLUGINS_CONFIG = { 68 | "nautobot_bgp_models": { 69 | "default_statuses": { 70 | "AutonomousSystem": ["Active", "Available", "Planned"], 71 | "BGPRoutingInstance": ["Planned", "Active", "Decommissioned"], 72 | "Peering": ["Active", "Decommissioned", "Deprovisioning", "Offline", "Planned", "Provisioning"], 73 | } 74 | } 75 | } 76 | ``` 77 | 78 | In the `default_statuses` section, you can define a list of default statuses to make available to `AutonomousSystem` and/or `Peering`. The lists must be composed of valid slugs of existing Status objects. 79 | -------------------------------------------------------------------------------- /docs/admin/release_notes/index.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | All the published release notes can be found via the navigation menu. All patch releases are included in the same minor release (e.g. `v1.2`) document. 4 | -------------------------------------------------------------------------------- /docs/admin/release_notes/version_0.20.md: -------------------------------------------------------------------------------- 1 | # v0.20 Release Notes 2 | 3 | ## Release Overview 4 | 5 | This version introduces support for Nautobot 2.0 6 | 7 | !!! warning 8 | This version contains breaking changes implemented in the Release 0.9 dedicated for Nautobot 1.x. Follow Release 0.9 docs for details. 9 | 10 | ## Version 0.20.0 11 | 12 | ### Added 13 | 14 | - [#109](https://github.com/nautobot/nautobot-plugin-bgp-models/pull/109) - Adds Nautobot 2.0 support 15 | 16 | ## Version 0.20.1 17 | 18 | ### Changed 19 | 20 | - [#150](https://github.com/nautobot/nautobot-plugin-bgp-models/pull/150) - Relaxed model validation of PeerEndpoint and PeerGroup to allow simultaneously setting `source_ip` and `source_interface` attributes. 21 | 22 | ### Fixed 23 | 24 | - [#156](https://github.com/nautobot/nautobot-plugin-bgp-models/issues/156) - Fixes a migration error when upgrading from Nautobot 1.x -------------------------------------------------------------------------------- /docs/admin/release_notes/version_0.7.md: -------------------------------------------------------------------------------- 1 | # v0.7 Release Notes 2 | 3 | ## Release Overview 4 | 5 | - Initial BGP Models release 6 | 7 | ## [v0.7.0-beta.1] - 2022-09-20 8 | 9 | ### Added 10 | 11 | ### Changed 12 | 13 | ### Fixed 14 | 15 | ## [v0.7.0] - 2023-05-16 16 | 17 | ### Added 18 | 19 | ### Changed 20 | - [#73](https://github.com/nautobot/nautobot-app-bgp-models/pull/73) - Documentation Update 21 | - [#90](https://github.com/nautobot/nautobot-app-bgp-models/pull/90) - Development environments updates 22 | 23 | ### Fixed 24 | 25 | - [#99](https://github.com/nautobot/nautobot-app-bgp-models/pull/99) - fix: allow read/write for extra attributes from API 26 | 27 | ## [v0.7.1] - 2023-05-19 28 | 29 | ### Added 30 | 31 | ### Changed 32 | 33 | ### Fixed 34 | 35 | - [#104](https://github.com/nautobot/nautobot-app-bgp-models/pull/104) - Multiple fixes for test cases and serializers 36 | -------------------------------------------------------------------------------- /docs/admin/release_notes/version_0.8.md: -------------------------------------------------------------------------------- 1 | # v0.8 Release Notes 2 | 3 | ## Release Overview 4 | 5 | - BGP Models v0.8 release 6 | 7 | ## [v0.8.0] - 2023-08-24 8 | 9 | ### Added 10 | - [#91](https://github.com/nautobot/nautobot-app-bgp-models/issues/91) - Implements Nautobot Viewsets for UI and API 11 | - [#91](https://github.com/nautobot/nautobot-app-bgp-models/issues/91) - Adds Nautobot Notes support 12 | - [#96](https://github.com/nautobot/nautobot-app-bgp-models/pull/96) - Adds CSV Imports / Exports 13 | - [#97](https://github.com/nautobot/nautobot-app-bgp-models/issues/97) - Adds `Status` field on `BGPRoutingInstance` model 14 | - [#114](https://github.com/nautobot/nautobot-app-bgp-models/pull/114) - Adds `Device Role` and `Peer Endpoint Role` filters on `Peering` model 15 | 16 | ### Changed 17 | - [#96](https://github.com/nautobot/nautobot-app-bgp-models/pull/96) - Renames field `template` to `peergroup_template` on `PeerGroup` model 18 | - [#116](https://github.com/nautobot/nautobot-app-bgp-models/pull/116) - Disables endpoint ordering in `Peering` table 19 | - [#122](https://github.com/nautobot/nautobot-app-bgp-models/pull/122) - Removes mandatory `Provider` field for external `Peer Endpoints` 20 | 21 | ### Removed 22 | - [#124](https://github.com/nautobot/nautobot-app-bgp-models/pull/124) - Removes support for Python 3.7 23 | -------------------------------------------------------------------------------- /docs/admin/release_notes/version_0.9.md: -------------------------------------------------------------------------------- 1 | # v0.9 Release Notes 2 | 3 | ## Release Overview 4 | 5 | This version introduces `PeerGroupAddressFamily` and `PeerEndpointAddressFamily` data models to provide for more granular configuration modeling. 6 | 7 | !!! warning 8 | This version **removes** the `import_policy`, `export_policy`, and `multipath` attributes from the `PeerGroupTemplate`, `PeerGroup`, and `PeerEndpoint` models, as these are generally address-family-specific configuration attributes and are modeled as such now. No data migration is provided at this time (as there is no way to identify **which** AFI-SAFI any existing policy/multipath configs should be migrated to), and upgrading to this version will therefore necessarily result in data loss if you had previously populated these model fields. Back up your configuration or record this data in some other format before upgrading if appropriate. 9 | 10 | ## Version 0.9.0 11 | 12 | ### Added 13 | 14 | - [#26](https://github.com/nautobot/nautobot-app-bgp-models/issues/26) - Adds `PeerGroupAddressFamily` and `PeerEndpointAddressFamily` data models. 15 | - [#132](https://github.com/nautobot/nautobot-app-bgp-models/pull/132) - Adds `extra_attributes` support to the `AddressFamily` model. 16 | 17 | ### Removed 18 | 19 | - [#132](https://github.com/nautobot/nautobot-app-bgp-models/pull/132) - Removes `import_policy`, `export_policy`, and `multipath` attributes from `PeerGroupTemplate`, `PeerGroup`, and `PeerEndpoint` models. Use the equivalent fields on `PeerGroupAddressFamily` and `PeerEndpointAddressFamily` instead. 20 | 21 | ### Dependencies 22 | 23 | - [#126](https://github.com/nautobot/nautobot-app-bgp-models/pull/126) - Updated development dependencies `mkdocstrings` and `mkdocstrings-python` to `0.22` and `1.4.0` respectively to address CI failures. 24 | 25 | ## Version 0.9.1 26 | 27 | ### Changed 28 | 29 | - [#150](https://github.com/nautobot/nautobot-app-bgp-models/pull/150) - Relaxed model validation of PeerEndpoint and PeerGroup to allow simultaneously setting `source_ip` and `source_interface` attributes. 30 | -------------------------------------------------------------------------------- /docs/admin/release_notes/version_1.0.md: -------------------------------------------------------------------------------- 1 | # v1.0 Release Notes 2 | 3 | ## Release Overview 4 | 5 | This version contains a fix for missing tags. 6 | 7 | ## Version 1.0 8 | 9 | ### Fixed 10 | 11 | - [#165](https://github.com/nautobot/nautobot-app-bgp-models/pull/165) - fix: 🐛 missing tags #165 12 | -------------------------------------------------------------------------------- /docs/admin/release_notes/version_2.0.md: -------------------------------------------------------------------------------- 1 | # v2.0 Release Notes 2 | 3 | ## Release Overview 4 | 5 | This release contains a fix for invalid ordering of PeerEndpoints in a Peering and a fix for missing tags. 6 | 7 | ## Version 2.0 8 | 9 | ### Fixed 10 | 11 | - [#169](https://github.com/nautobot/nautobot-app-bgp-models/pull/169) - Implement ordering of PeerEndpoints in Peering 12 | - [#177](https://github.com/nautobot/nautobot-app-bgp-models/pull/177) - fix: 🐛 missing tags in serializers (Nautobot 2.x) 13 | -------------------------------------------------------------------------------- /docs/admin/release_notes/version_2.1.md: -------------------------------------------------------------------------------- 1 | # v2.1 Release Notes 2 | 3 | ## Release Overview 4 | 5 | This release introduces ASN Range models. 6 | 7 | ## Version 2.1 8 | 9 | ### Added 10 | 11 | - [#146](https://github.com/nautobot/nautobot-app-bgp-models/pull/146) - Add device/device_id filters to the PeerGroup, Peering, and PeerEndpoint filtersets where they were missing. 12 | - [#148](https://github.com/nautobot/nautobot-app-bgp-models/pull/148) - Add tags to the fields on PeerGroupTemplateForm 13 | - [#183](https://github.com/nautobot/nautobot-app-bgp-models/pull/183) - Add ASN Range models 14 | -------------------------------------------------------------------------------- /docs/admin/release_notes/version_2.2.md: -------------------------------------------------------------------------------- 1 | # v2.2 Release Notes 2 | 3 | This document describes all new features and changes in the release `2.2`. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 4 | 5 | ## Release Overview 6 | 7 | This release introduces the ability to use ASDOT ASN notation. It also adds support for Nautobot v2.3.0. 8 | 9 | 10 | ## [v2.2.0 (2024-08-07)](https://github.com/nautobot/nautobot-app-bgp-models/releases/tag/v2.2.0) 11 | 12 | ### Added 13 | 14 | - [#29](https://github.com/nautobot/nautobot-app-bgp-models/issues/29) - Added support for ASNs ASDOT notation (RFC 5396). 15 | - [#215](https://github.com/nautobot/nautobot-app-bgp-models/issues/215) - Added Django 4 support. 16 | -------------------------------------------------------------------------------- /docs/admin/release_notes/version_2.3.md: -------------------------------------------------------------------------------- 1 | 2 | # v2.3 Release Notes 3 | 4 | This document describes all new features and changes in the release. The format is based on [Keep a 5 | Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic 6 | Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## Release Overview 9 | 10 | - Python 3.12 Support 11 | 12 | ## [v2.3.0 (2024-12-02)](https://github.com/nautobot/nautobot-app-bgp-models/releases/tag/v2.3.0) 13 | 14 | ### Added 15 | 16 | - [#221](https://github.com/nautobot/nautobot-app-bgp-models/issues/221) - Added support for Python 3.12. 17 | 18 | ### Housekeeping 19 | 20 | - Rebaked from the cookie `nautobot-app-v2.4.0`. 21 | - [#199](https://github.com/nautobot/nautobot-app-bgp-models/issues/199) - Updated the documentation to be in sync with the latest default configuration. 22 | - [#219](https://github.com/nautobot/nautobot-app-bgp-models/issues/219) - Rebaked from the cookie `nautobot-app-v2.3.0`. 23 | - [#221](https://github.com/nautobot/nautobot-app-bgp-models/issues/221) - Rebaked from the cookie `nautobot-app-v2.3.2`. 24 | - [#225](https://github.com/nautobot/nautobot-app-bgp-models/issues/225) - Changed model_class_name in .cookiecutter.json to a valid model to help with drift management. 25 | -------------------------------------------------------------------------------- /docs/admin/uninstall.md: -------------------------------------------------------------------------------- 1 | # Uninstall the App from Nautobot 2 | 3 | Here you will find any steps necessary to cleanly remove the App from your Nautobot environment. 4 | 5 | ## Database Cleanup 6 | 7 | Prior to removing the app from the `nautobot_config.py`, run the following command to roll back any migration specific to this app. 8 | 9 | ```shell 10 | nautobot-server migrate nautobot_bgp_models zero 11 | ``` 12 | 13 | ## Remove App configuration 14 | 15 | Remove the configuration you added in `nautobot_config.py` from `PLUGINS` & `PLUGINS_CONFIG`. 16 | 17 | ## Uninstall the package 18 | 19 | ```bash 20 | $ pip3 uninstall nautobot-bgp-models 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/admin/upgrade.md: -------------------------------------------------------------------------------- 1 | # Upgrading the App 2 | 3 | Here you will find any steps necessary to upgrade the App in your Nautobot environment. 4 | 5 | ## Upgrade Guide 6 | 7 | When a new release comes out it may be necessary to run a migration of the database to account for any changes in the data models used by this app. Execute the command `nautobot-server post-upgrade` within the runtime environment of your Nautobot installation after updating the `nautobot-bgp-models` package via `pip`. 8 | -------------------------------------------------------------------------------- /docs/assets/extra.css: -------------------------------------------------------------------------------- 1 | :root>* { 2 | --md-accent-fg-color: #ff8504; 3 | --md-primary-fg-color: #ff8504; 4 | --md-typeset-a-color: #0097ff; 5 | } 6 | 7 | [data-md-color-scheme="slate"] { 8 | --md-default-bg-color: hsla(var(--md-hue), 0%, 15%, 1); 9 | --md-typeset-a-color: #0097ff; 10 | } 11 | 12 | /* Accessibility: Increase fonts for dark theme */ 13 | [data-md-color-scheme="slate"] .md-typeset { 14 | font-size: 0.9rem; 15 | } 16 | 17 | [data-md-color-scheme="slate"] .md-typeset table:not([class]) { 18 | font-size: 0.7rem; 19 | } 20 | 21 | /* 22 | * The default max-width is 61rem which does not provide nearly enough space to present code examples or larger tables 23 | */ 24 | .md-grid { 25 | margin-left: auto; 26 | margin-right: auto; 27 | max-width: 95%; 28 | } 29 | 30 | .md-tabs__link { 31 | font-size: 0.8rem; 32 | } 33 | 34 | .md-tabs__link--active { 35 | color: var(--md-primary-fg-color); 36 | } 37 | 38 | .md-header__button.md-logo :is(img, svg) { 39 | height: 2rem; 40 | } 41 | 42 | .md-header__button.md-logo :-webkit-any(img, svg) { 43 | height: 2rem; 44 | } 45 | 46 | .md-header__title { 47 | font-size: 1.2rem; 48 | } 49 | 50 | img.logo { 51 | height: 200px; 52 | } 53 | 54 | img.copyright-logo { 55 | height: 24px; 56 | vertical-align: middle; 57 | } 58 | 59 | [data-md-color-primary=black] .md-header { 60 | background-color: #212121; 61 | } 62 | 63 | @media screen and (min-width: 76.25em) { 64 | [data-md-color-primary=black] .md-tabs { 65 | background-color: #212121; 66 | } 67 | } 68 | 69 | /* Customization for mkdocstrings */ 70 | /* Indentation. */ 71 | div.doc-contents:not(.first) { 72 | padding-left: 25px; 73 | border-left: .2rem solid var(--md-typeset-table-color); 74 | } 75 | 76 | /* Mark external links as such. */ 77 | a.autorefs-external::after { 78 | /* https://primer.style/octicons/arrow-up-right-24 */ 79 | background-image: url('data:image/svg+xml,'); 80 | content: ' '; 81 | 82 | display: inline-block; 83 | position: relative; 84 | top: 0.1em; 85 | margin-left: 0.2em; 86 | margin-right: 0.1em; 87 | 88 | height: 1em; 89 | width: 1em; 90 | border-radius: 100%; 91 | background-color: var(--md-typeset-a-color); 92 | } 93 | 94 | a.autorefs-external:hover::after { 95 | background-color: var(--md-accent-fg-color); 96 | } 97 | 98 | 99 | /* Customization for markdown-version-annotations */ 100 | :root { 101 | /* Icon for "version-added" admonition: Material Design Icons "plus-box-outline" */ 102 | --md-admonition-icon--version-added: url('data:image/svg+xml;charset=utf-8,'); 103 | /* Icon for "version-changed" admonition: Material Design Icons "delta" */ 104 | --md-admonition-icon--version-changed: url('data:image/svg+xml;charset=utf-8,'); 105 | /* Icon for "version-removed" admonition: Material Design Icons "minus-circle-outline" */ 106 | --md-admonition-icon--version-removed: url('data:image/svg+xml;charset=utf-8,'); 107 | } 108 | 109 | /* "version-added" admonition in green */ 110 | .md-typeset .admonition.version-added, 111 | .md-typeset details.version-added { 112 | border-color: rgb(0, 200, 83); 113 | } 114 | 115 | .md-typeset .version-added>.admonition-title, 116 | .md-typeset .version-added>summary { 117 | background-color: rgba(0, 200, 83, .1); 118 | } 119 | 120 | .md-typeset .version-added>.admonition-title::before, 121 | .md-typeset .version-added>summary::before { 122 | background-color: rgb(0, 200, 83); 123 | -webkit-mask-image: var(--md-admonition-icon--version-added); 124 | mask-image: var(--md-admonition-icon--version-added); 125 | } 126 | 127 | /* "version-changed" admonition in orange */ 128 | .md-typeset .admonition.version-changed, 129 | .md-typeset details.version-changed { 130 | border-color: rgb(255, 145, 0); 131 | } 132 | 133 | .md-typeset .version-changed>.admonition-title, 134 | .md-typeset .version-changed>summary { 135 | background-color: rgba(255, 145, 0, .1); 136 | } 137 | 138 | .md-typeset .version-changed>.admonition-title::before, 139 | .md-typeset .version-changed>summary::before { 140 | background-color: rgb(255, 145, 0); 141 | -webkit-mask-image: var(--md-admonition-icon--version-changed); 142 | mask-image: var(--md-admonition-icon--version-changed); 143 | } 144 | 145 | /* "version-removed" admonition in red */ 146 | .md-typeset .admonition.version-removed, 147 | .md-typeset details.version-removed { 148 | border-color: rgb(255, 82, 82); 149 | } 150 | 151 | .md-typeset .version-removed>.admonition-title, 152 | .md-typeset .version-removed>summary { 153 | background-color: rgba(255, 82, 82, .1); 154 | } 155 | 156 | .md-typeset .version-removed>.admonition-title::before, 157 | .md-typeset .version-removed>summary::before { 158 | background-color: rgb(255, 82, 82); 159 | -webkit-mask-image: var(--md-admonition-icon--version-removed); 160 | mask-image: var(--md-admonition-icon--version-removed); 161 | } 162 | 163 | /* Do not wrap code blocks in markdown tables. */ 164 | div.md-typeset__table>table>tbody>tr>td>code { 165 | white-space: nowrap; 166 | } 167 | -------------------------------------------------------------------------------- /docs/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/assets/favicon.ico -------------------------------------------------------------------------------- /docs/assets/nautobot_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/assets/nautobot_logo.png -------------------------------------------------------------------------------- /docs/assets/networktocode_bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/assets/networktocode_bw.png -------------------------------------------------------------------------------- /docs/assets/overrides/partials/copyright.html: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 21 |
22 | 23 | -------------------------------------------------------------------------------- /docs/dev/code_reference/api.md: -------------------------------------------------------------------------------- 1 | # BGP Models API Package 2 | 3 | ::: nautobot_bgp_models.api 4 | options: 5 | show_submodules: True 6 | -------------------------------------------------------------------------------- /docs/dev/code_reference/index.md: -------------------------------------------------------------------------------- 1 | # Code Reference 2 | 3 | Auto-generated code reference documentation from docstrings. 4 | -------------------------------------------------------------------------------- /docs/dev/code_reference/package.md: -------------------------------------------------------------------------------- 1 | ::: nautobot_bgp_models 2 | -------------------------------------------------------------------------------- /docs/dev/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to the App 2 | 3 | The project is packaged with a light [development environment](dev_environment.md) based on `docker-compose` to help with the local development of the project and to run tests. 4 | 5 | The project is following Network to Code software development guidelines and is leveraging the following: 6 | 7 | - Python linting and formatting: `pylint` and `ruff`. 8 | - YAML linting is done with `yamllint`. 9 | - Django unit test to ensure the app is working properly. 10 | 11 | Documentation is built using [mkdocs](https://www.mkdocs.org/). The [Docker based development environment](dev_environment.md#docker-development-environment) automatically starts a container hosting a live version of the documentation website on [http://localhost:8001](http://localhost:8001) that auto-refreshes when you make any changes to your local files. 12 | 13 | ## Creating Changelog Fragments 14 | 15 | All pull requests to `next` or `develop` must include a changelog fragment file in the `./changes` directory. To create a fragment, use your GitHub issue number and fragment type as the filename. For example, `2362.added`. Valid fragment types are `added`, `changed`, `deprecated`, `fixed`, `removed`, and `security`. The change summary is added to the file in plain text. Change summaries should be complete sentences, starting with a capital letter and ending with a period, and be in past tense. Each line of the change fragment will generate a single change entry in the release notes. Use multiple lines in the same file if your change needs to generate multiple release notes in the same category. If the change needs to create multiple entries in separate categories, create multiple files. 16 | 17 | !!! example 18 | 19 | **Wrong** 20 | ```plaintext title="changes/1234.fixed" 21 | fix critical bug in documentation 22 | ``` 23 | 24 | **Right** 25 | ```plaintext title="changes/1234.fixed" 26 | Fixed critical bug in documentation. 27 | ``` 28 | 29 | !!! example "Multiple Entry Example" 30 | 31 | This will generate 2 entries in the `fixed` category and one entry in the `changed` category. 32 | 33 | ```plaintext title="changes/1234.fixed" 34 | Fixed critical bug in documentation. 35 | Fixed release notes generation. 36 | ``` 37 | 38 | ```plaintext title="changes/1234.changed" 39 | Changed release notes generation. 40 | ``` 41 | 42 | ## Branching Policy 43 | 44 | The branching policy includes the following tenets: 45 | 46 | - The `develop` branch is the branch of the next major and minor paired version planned. 47 | - PRs intended to add new features should be sourced from the `develop` branch. 48 | - PRs intended to fix issues in the Nautobot LTM compatible release should be sourced from the latest `ltm-` branch instead of `develop`. 49 | 50 | BGP Models will observe semantic versioning, as of 1.0. This may result in a quick turnaround in minor versions to keep pace with an ever-growing feature set. 51 | 52 | ### Backporting to Older Releases 53 | 54 | If you are backporting any fixes to a prior major or minor version of this app, please open an issue, comment on an existing issue, or post in the [Network to Code Slack](https://networktocode.slack.com/) (channel `#nautobot`). 55 | 56 | We will create a `release-X.Y` branch for you to open your PR against and cut a new release once the PR is successfully merged. 57 | 58 | ## Release Policy 59 | 60 | BGP Models has currently no intended scheduled release schedule, and will release new features in minor versions. 61 | 62 | The steps taken by maintainers when creating a new release are documented in the [release checklist](./release_checklist.md). 63 | -------------------------------------------------------------------------------- /docs/dev/extending.md: -------------------------------------------------------------------------------- 1 | # Extending the App 2 | 3 | Extending the application is welcome, however it is best to open an issue first, to ensure that a PR would be accepted and makes sense in terms of features and design. 4 | -------------------------------------------------------------------------------- /docs/images/add_asn_12345.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/add_asn_12345.png -------------------------------------------------------------------------------- /docs/images/add_external_peering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/add_external_peering.png -------------------------------------------------------------------------------- /docs/images/add_internal_peering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/add_internal_peering.png -------------------------------------------------------------------------------- /docs/images/add_new_ri.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/add_new_ri.png -------------------------------------------------------------------------------- /docs/images/autonomous_system_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/autonomous_system_01.png -------------------------------------------------------------------------------- /docs/images/external_peering_created.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/external_peering_created.png -------------------------------------------------------------------------------- /docs/images/icon-nautobot-bgp-models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/icon-nautobot-bgp-models.png -------------------------------------------------------------------------------- /docs/images/internal_peering_created.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/internal_peering_created.png -------------------------------------------------------------------------------- /docs/images/main-page-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/main-page-menu.png -------------------------------------------------------------------------------- /docs/images/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/menu.png -------------------------------------------------------------------------------- /docs/images/peer_endpoint_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/peer_endpoint_01.png -------------------------------------------------------------------------------- /docs/images/peer_group_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/peer_group_01.png -------------------------------------------------------------------------------- /docs/images/peering_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/peering_01.png -------------------------------------------------------------------------------- /docs/images/peering_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/peering_list.png -------------------------------------------------------------------------------- /docs/images/ri_list_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/docs/images/ri_list_view.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | 4 | - navigation 5 | 6 | --- 7 | 8 | --8<-- "README.md" 9 | -------------------------------------------------------------------------------- /docs/models/autonomoussystem.md: -------------------------------------------------------------------------------- 1 | # Autonomous System 2 | 3 | This model represents a network-wide description of a BGP autonomous system (AS). It has fields including the actual AS number (ASN), a description field, foreign key (FK) to a Nautobot `Provider` object, and a FK to a Nautobot `Status` object. 4 | 5 | - `asn` (ASNField): 32-bit autonomous system number. 6 | - `description`: (string): Description for the autonomous system. 7 | - `provider`: (Provider): Provider for the autonomous system. 8 | - `status`: (Status): Status for the autonomous system. 9 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs==1.6.0 2 | mkdocs-material==9.5.32 3 | markdown-version-annotations==1.0.1 4 | griffe==1.1.1 5 | mkdocstrings-python==1.10.8 6 | mkdocstrings==0.25.2 7 | mkdocs-autorefs==1.2.0 8 | -------------------------------------------------------------------------------- /docs/user/app_getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting Started with the App 2 | 3 | This document provides a step-by-step tutorial on how to get the App going and how to use it. 4 | 5 | ## Install the App 6 | 7 | To install the App, please follow the instructions detailed in the [Installation Guide](../admin/install.md). 8 | 9 | ## First steps with the App 10 | 11 | We will now explore the workflows for the two most common BGP App use cases: modeling internal and external peerings. We will showcase how to create each type of peering with the minimum number of steps and inputs required from users. 12 | 13 | ### Menu Item 14 | 15 | All of the "Add object" actions are available under the Routing menu in the top navigation bar: 16 | 17 | ![Routing menu](../images/menu.png "Routing Menu") 18 | 19 | ### Internal Peering Creation 20 | 21 | To model an internal peering (two devices sharing the same ASN), following has to be defined for two BGP speaker devices: 22 | 23 | - a BGP Routing Instance 24 | - IP Address of an endpoint 25 | 26 | !!! Note 27 | Having a BGP Routing Instance is not mandatory, however we recommend creating this object for devices modeled in Nautobot. 28 | 29 | #### Autonomous System Creation 30 | 31 | The first step is to add an Autonomous System object (via the top menu). 32 | 33 | Fill the object details: 34 | 35 | ![Autonomous System Form](../images/add_asn_12345.png "Autonomous System Form") 36 | 37 | #### BGP Routing Instances creation 38 | 39 | The next step is to create a BGP Routing Instance for each device of an internal BGP peering. 40 | A BGP Routing Instance itself is a representation (or a declaration) of a BGP process on a given device. 41 | 42 | Fill the object details: 43 | 44 | ![BGP Routing Instance Form](../images/add_new_ri.png "BGP Routing Instance Form") 45 | 46 | Repeat for next devices and check the overall result in the BGP Routing Instance list view: 47 | 48 | ![BGP Routing Instances List](../images/ri_list_view.png "BGP Routing Instances List") 49 | 50 | #### Peering creation 51 | 52 | Under the menu "BGP Peerings - Peerings" click on the "Add" button to add a new peering. 53 | You will be redirected to a view with two columns in a table, each column representing one side of a BGP peering. 54 | To create a BGP Peering, you have to complete information for two sides. 55 | 56 | ![BGP Peering Form](../images/add_internal_peering.png "BGP Peering Form") 57 | 58 | To create an internal BGP Peering, you only need to specify an existing BGP Routing Instance and an IP Address. 59 | 60 | #### Peering detail view 61 | 62 | Once the BGP Peering is created, you could review its details. 63 | 64 | ![BGP Peering Details](../images/internal_peering_created.png "BGP Peering Details") 65 | 66 | 67 | ### External Peering Creation 68 | 69 | To model an external peering (two devices having different ASN), the following has to be defined: 70 | 71 | - For a device present in Nautobot: 72 | - a BGP Routing Instance 73 | - IP Address of an endpoint 74 | 75 | - For a device not present in Nautobot: 76 | - an Autonomous System 77 | - IP Address of an endpoint 78 | 79 | The steps required to create an internal peer have been explained in the previous section. 80 | 81 | #### Autonomous System Creation - for Provider 82 | 83 | The first step is to add an Autonomous System object for a Provider. 84 | Fill in the object details and ensure the optional field "Provider" is filled. 85 | 86 | #### Peering creation 87 | 88 | Once the Autonomous System and BGP Routing Instance objects have been created you are ready to create a peering between two devices. 89 | Under the menu "BGP Peerings - Peerings" click on the "Add" button to add a new peering. 90 | You will be redirected to a view with two columns in a table, each column representing one side of a BGP peering. 91 | To create a BGP Peering, You have to complete information for both sides. 92 | 93 | ![BGP Peering Form](../images/add_external_peering.png "BGP Peering Form") 94 | 95 | To create an external BGP Peering, for the Provider's side You have to fill in the information with the Provider's ASN and IP Address of the provider's endpoint. 96 | 97 | #### Peering detail view 98 | 99 | Once the BGP Peering is created, you could review its details. 100 | 101 | ![BGP Peering Details](../images/external_peering_created.png "BGP Peering Details") 102 | 103 | 104 | 105 | ## What are the next steps? 106 | 107 | Check out the [Cisco](cisco_use_case.md) or [Juniper](juniper_use_case.md) configuration example use-cases. 108 | -------------------------------------------------------------------------------- /docs/user/app_overview.md: -------------------------------------------------------------------------------- 1 | # App Overview 2 | 3 | This document provides an overview of the App including critical information and important considerations when applying it to your Nautobot environment. 4 | 5 | !!! note 6 | Throughout this documentation, the terms "app" and "plugin" will be used interchangeably. 7 | 8 | ## Description 9 | 10 | An app for [Nautobot](https://github.com/nautobot/nautobot), extending the core models with BGP-specific models. They enable modeling and management of BGP peerings, whether or not the peer device is present in Nautobot. 11 | 12 | This application adds the following new data models into Nautobot: 13 | 14 | - **Autonomous System**: network-wide description of a BGP autonomous system (AS) 15 | - **Peering Role**: describes the valid options for PeerGroup, PeerGroupTemplate, and/or Peering roles 16 | - **BGP Routing Instance**: device-specific BGP process 17 | - **Address Family**: device-specific configuration of a BGP address family (AFI-SAFI) with an optional VRF 18 | - **Peer Group Template**: network-wide template for Peer Group objects 19 | - **Peer Group**: device-specific configuration for a group of functionally related BGP peers 20 | - **Peer Group Address Family**: peer-group-specific configuration of a BGP address-family (AFI-SAFI) 21 | - **Peering and Peer Endpoints**: A BGP Peering is represented by a Peering object and two endpoints, each representing the configuration of one side of the BGP peering. A Peer Endpoint must be associated with a BGP Routing Instance. 22 | - **Peer Endpoint Address Family**: peer-specific configuration of a BGP address-family (AFI-SAFI) 23 | 24 | With these new models, it's now possible to populate the Source of Truth (SoT) with any BGP peerings, internal or external, regardless of whether both endpoints are fully defined in the Source of Truth. 25 | 26 | The minimum requirement to define a BGP peering is two IP addresses and one or two autonomous systems (one ASN for iBGP, two ASNs for eBGP). 27 | 28 | ## Audience (User Personas) - Who should use this App? 29 | 30 | Network Admins who need to model their BGP internal and external peerings inside of their Source of Truth so they can programmatically generate BGP configuration for their devices. 31 | 32 | ## Authors and Maintainers 33 | 34 | - Glenn Matthews (@glennmatthews) 35 | - Marek Zbroch (@mzbroch) 36 | 37 | ## Nautobot Features Used 38 | 39 | This app adds the following data models to Nautobot: 40 | 41 | - AutonomousSystem 42 | - PeeringRole 43 | - BGPRoutingInstance 44 | - AddressFamily 45 | - PeerGroupTemplate 46 | - PeerGroup 47 | - PeerGroupAddressFamily 48 | - PeerEndpoint 49 | - PeerEndpointAddressFamily 50 | - Peering 51 | 52 | The data models introduced by the BGP app support the following Nautobot features: 53 | 54 | - Rest API 55 | - GraphQL 56 | - Custom fields 57 | - Custom Links 58 | - Relationships 59 | - Change logging 60 | - Custom data validation logic 61 | - Webhooks 62 | 63 | For more details please visit the [BGP Data Models](../dev/models.md) page. 64 | -------------------------------------------------------------------------------- /docs/user/app_use_cases.md: -------------------------------------------------------------------------------- 1 | # Using the App 2 | 3 | This document describes common use-cases and scenarios for this App. 4 | 5 | ## Use-cases and common workflows 6 | 7 | To make getting started with the app easier, we provide example use-cases for two common OS platforms: Cisco and Juniper. 8 | 9 | ### Cisco Configuration Modeling and Rendering 10 | 11 | Navigate to [Cisco Example Use Case](cisco_use_case.md) for detailed instructions how to consume BGP Models app on Cisco devices. 12 | 13 | ### Juniper Configuration Modeling and Rendering 14 | 15 | Navigate to [Juniper Example Use Case](juniper_use_case.md) for detailed instructions how to consume BGP Models app on Juniper devices. 16 | 17 | ## Screenshots 18 | 19 | ### Routing Menu 20 | 21 | ![Menu](../images/main-page-menu.png) 22 | 23 | ### Autonomous System 24 | 25 | ![Autonomous System](../images/autonomous_system_01.png) 26 | 27 | ### Peering List 28 | 29 | ![Peering List](../images/peering_list.png) 30 | 31 | ### BGP Peering 32 | 33 | ![Peering](../images/peering_01.png) 34 | 35 | ### Peer Endpoint 36 | 37 | ![Peer Endpoint](../images/peer_endpoint_01.png) 38 | 39 | ### Peer Group 40 | 41 | ![Peer Group](../images/peer_group_01.png) 42 | -------------------------------------------------------------------------------- /docs/user/faq.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | -------------------------------------------------------------------------------- /invoke.example.yml: -------------------------------------------------------------------------------- 1 | --- 2 | nautobot_bgp_models: 3 | nautobot_ver: "2.0.3" 4 | python_ver: "3.11" 5 | # local: false 6 | # compose_dir: "/full/path/to/nautobot-app-bgp-models/development" 7 | 8 | # The following is an example of using MySQL as the database backend 9 | # --- 10 | # nautobot_bgp_models: 11 | # compose_files: 12 | # - "docker-compose.base.yml" 13 | # - "docker-compose.redis.yml" 14 | # - "docker-compose.mysql.yml" 15 | # - "docker-compose.dev.yml" 16 | -------------------------------------------------------------------------------- /invoke.mysql.yml: -------------------------------------------------------------------------------- 1 | --- 2 | nautobot_bgp_models: 3 | project_name: "nautobot-bgp-models" 4 | nautobot_ver: "2.0.3" 5 | local: false 6 | python_ver: "3.11" 7 | compose_dir: "development" 8 | compose_files: 9 | - "docker-compose.base.yml" 10 | - "docker-compose.redis.yml" 11 | - "docker-compose.mysql.yml" 12 | - "docker-compose.dev.yml" 13 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dev_addr: "127.0.0.1:8001" 3 | edit_uri: "edit/main/docs" 4 | site_dir: "nautobot_bgp_models/static/nautobot_bgp_models/docs" 5 | site_name: "BGP Models Documentation" 6 | site_url: "https://docs.nautobot.com/projects/bgp-models/en/latest/" 7 | repo_url: "https://github.com/nautobot/nautobot-app-bgp-models" 8 | copyright: "Copyright © The Authors" 9 | theme: 10 | name: "material" 11 | navigation_depth: 4 12 | custom_dir: "docs/assets/overrides" 13 | hljs_languages: 14 | - "django" 15 | - "yaml" 16 | features: 17 | - "content.action.edit" 18 | - "content.action.view" 19 | - "content.code.copy" 20 | - "navigation.footer" 21 | - "navigation.indexes" 22 | - "navigation.tabs" 23 | - "navigation.tabs.sticky" 24 | - "navigation.tracking" 25 | - "search.highlight" 26 | - "search.share" 27 | - "search.suggest" 28 | favicon: "assets/favicon.ico" 29 | logo: "assets/nautobot_logo.svg" 30 | palette: 31 | # Palette toggle for light mode 32 | - media: "(prefers-color-scheme: light)" 33 | scheme: "default" 34 | primary: "black" 35 | toggle: 36 | icon: "material/weather-sunny" 37 | name: "Switch to dark mode" 38 | 39 | # Palette toggle for dark mode 40 | - media: "(prefers-color-scheme: dark)" 41 | scheme: "slate" 42 | primary: "black" 43 | toggle: 44 | icon: "material/weather-night" 45 | name: "Switch to light mode" 46 | extra_css: 47 | - "assets/extra.css" 48 | 49 | extra: 50 | generator: false 51 | ntc_sponsor: true 52 | social: 53 | - icon: "fontawesome/solid/rss" 54 | link: "https://blog.networktocode.com/blog/tags/nautobot" 55 | name: "Network to Code Blog" 56 | - icon: "fontawesome/brands/youtube" 57 | link: "https://www.youtube.com/playlist?list=PLjA0bhxgryJ2Ts4GJMDA-tPzVWEncv4pb" 58 | name: "Nautobot Videos" 59 | - icon: "fontawesome/brands/slack" 60 | link: "https://www.networktocode.com/community/" 61 | name: "Network to Code Community" 62 | - icon: "fontawesome/brands/github" 63 | link: "https://github.com/nautobot/nautobot" 64 | name: "GitHub Repo" 65 | - icon: "fontawesome/brands/twitter" 66 | link: "https://twitter.com/networktocode" 67 | name: "Network to Code Twitter" 68 | markdown_extensions: 69 | - "markdown_version_annotations": 70 | admonition_tag: "???" 71 | - "admonition" 72 | - "toc": 73 | permalink: true 74 | - "attr_list" 75 | - "md_in_html" 76 | - "pymdownx.highlight": 77 | anchor_linenums: true 78 | - "pymdownx.inlinehilite" 79 | - "pymdownx.snippets" 80 | - "pymdownx.superfences": 81 | custom_fences: 82 | - name: "mermaid" 83 | class: "mermaid" 84 | format: !!python/name:pymdownx.superfences.fence_code_format 85 | - "footnotes" 86 | plugins: 87 | - "search" 88 | - "mkdocstrings": 89 | default_handler: "python" 90 | handlers: 91 | python: 92 | paths: ["."] 93 | options: 94 | show_root_heading: true 95 | watch: 96 | - "README.md" 97 | 98 | validation: 99 | omitted_files: "warn" 100 | absolute_links: "warn" 101 | unrecognized_links: "warn" 102 | anchors: "warn" 103 | 104 | nav: 105 | - Overview: "index.md" 106 | - User Guide: 107 | - App Overview: "user/app_overview.md" 108 | - Getting Started: "user/app_getting_started.md" 109 | - Using the App: "user/app_use_cases.md" 110 | - Cisco Example Use Case: "user/cisco_use_case.md" 111 | - Juniper Example Use Case: "user/juniper_use_case.md" 112 | - Frequently Asked Questions: "user/faq.md" 113 | - Data Models: 114 | - Autonomous System: "models/autonomoussystem.md" 115 | - Administrator Guide: 116 | - Install and Configure: "admin/install.md" 117 | - Upgrade: "admin/upgrade.md" 118 | - Uninstall: "admin/uninstall.md" 119 | - Compatibility Matrix: "admin/compatibility_matrix.md" 120 | - Release Notes: 121 | - "admin/release_notes/index.md" 122 | - v0.7: "admin/release_notes/version_0.7.md" 123 | - v0.8: "admin/release_notes/version_0.8.md" 124 | - v0.9: "admin/release_notes/version_0.9.md" 125 | - v0.20: "admin/release_notes/version_0.20.md" 126 | - v1.0: "admin/release_notes/version_1.0.md" 127 | - v2.0: "admin/release_notes/version_2.0.md" 128 | - v2.1: "admin/release_notes/version_2.1.md" 129 | - v2.2: "admin/release_notes/version_2.2.md" 130 | - v2.3: "admin/release_notes/version_2.3.md" 131 | - Developer Guide: 132 | - BGP Data Models: "dev/models.md" 133 | - Extending the App: "dev/extending.md" 134 | - Contributing to the App: "dev/contributing.md" 135 | - Development Environment: "dev/dev_environment.md" 136 | - Release Checklist: "dev/release_checklist.md" 137 | - Code Reference: 138 | - "dev/code_reference/index.md" 139 | - Package: "dev/code_reference/package.md" 140 | - API: "dev/code_reference/api.md" 141 | - Nautobot Docs Home ↗︎: "https://docs.nautobot.com" 142 | -------------------------------------------------------------------------------- /nautobot_bgp_models/__init__.py: -------------------------------------------------------------------------------- 1 | """App declaration for nautobot_bgp_models.""" 2 | 3 | # Metadata is inherited from Nautobot. If not including Nautobot in the environment, this should be added 4 | from importlib import metadata 5 | 6 | from django.db.models.signals import post_migrate 7 | from nautobot.apps import NautobotAppConfig 8 | 9 | __version__ = metadata.version(__name__) 10 | 11 | 12 | class NautobotBGPModelsConfig(NautobotAppConfig): 13 | """App configuration for the nautobot_bgp_models app.""" 14 | 15 | name = "nautobot_bgp_models" 16 | verbose_name = "BGP Models" 17 | version = __version__ 18 | author = "Network to Code, LLC" 19 | description = "Nautobot BGP Models App." 20 | base_url = "bgp" 21 | required_settings = [] 22 | min_version = "2.0.3" 23 | max_version = "2.9999" 24 | default_settings = { 25 | "default_statuses": { 26 | "AutonomousSystem": ["Active", "Available", "Planned"], 27 | "BGPRoutingInstance": ["Planned", "Active", "Decommissioned"], 28 | "Peering": ["Active", "Decommissioned", "Deprovisioning", "Offline", "Planned", "Provisioning"], 29 | } 30 | } 31 | caching_config = {} 32 | docs_view_name = "plugins:nautobot_bgp_models:docs" 33 | 34 | def ready(self): 35 | """Callback invoked after the app is loaded.""" 36 | super().ready() 37 | 38 | # Attempt to register versioned models & tables with Dolt if it is 39 | # available. 40 | from . import dolt_compat # noqa pylint: disable=import-outside-toplevel, unused-import 41 | 42 | from .signals import ( # pylint: disable=import-outside-toplevel 43 | post_migrate_create_statuses, 44 | ) 45 | 46 | post_migrate.connect(post_migrate_create_statuses, sender=self) 47 | 48 | 49 | config = NautobotBGPModelsConfig # pylint:disable=invalid-name 50 | -------------------------------------------------------------------------------- /nautobot_bgp_models/api/__init__.py: -------------------------------------------------------------------------------- 1 | """REST API module for nautobot_bgp_models app.""" 2 | -------------------------------------------------------------------------------- /nautobot_bgp_models/api/filter_backends.py: -------------------------------------------------------------------------------- 1 | """Filter Backends in use by BGP models app.""" 2 | 3 | from nautobot.core.api.filter_backends import NautobotFilterBackend 4 | 5 | 6 | class IncludeInheritedFilterBackend(NautobotFilterBackend): 7 | """ 8 | Used by views that work with inheritance (PeerGroupViewSet, PeerEndpointViewSet). 9 | 10 | Recognizes that "include_inherited" is not a filterset parameter but rather a view parameter (see InheritableFieldsViewSetMixin) 11 | """ 12 | 13 | def get_filterset_kwargs(self, request, queryset, view): 14 | """Pop include_inherited as it is not a valid filter field.""" 15 | kwargs = super().get_filterset_kwargs(request, queryset, view) 16 | try: 17 | kwargs["data"].pop("include_inherited") 18 | except KeyError: 19 | pass 20 | return kwargs 21 | -------------------------------------------------------------------------------- /nautobot_bgp_models/api/serializers.py: -------------------------------------------------------------------------------- 1 | """API serializers for nautobot_bgp_models.""" 2 | 3 | from nautobot.apps.api import ( 4 | NautobotModelSerializer, 5 | TaggedModelSerializerMixin, 6 | ) 7 | from nautobot.core.settings_funcs import is_truthy 8 | from rest_framework import serializers, validators 9 | 10 | from nautobot_bgp_models import models 11 | 12 | 13 | class AutonomousSystemSerializer( 14 | NautobotModelSerializer, 15 | TaggedModelSerializerMixin, 16 | ): 17 | """REST API serializer for AutonomousSystem records.""" 18 | 19 | url = serializers.HyperlinkedIdentityField(view_name="plugins-api:nautobot_bgp_models-api:autonomoussystem-detail") 20 | 21 | class Meta: 22 | model = models.AutonomousSystem 23 | fields = "__all__" 24 | 25 | 26 | class AutonomousSystemRangeSerializer( 27 | NautobotModelSerializer, 28 | TaggedModelSerializerMixin, 29 | ): 30 | """REST API serializer for AutonomousSystemRange records.""" 31 | 32 | url = serializers.HyperlinkedIdentityField( 33 | view_name="plugins-api:nautobot_bgp_models-api:autonomoussystemrange-detail" 34 | ) 35 | 36 | class Meta: 37 | model = models.AutonomousSystemRange 38 | fields = "__all__" 39 | 40 | 41 | class InheritableFieldsSerializerMixin: 42 | """Common mixin for Serializers that support an additional `include_inherited` query parameter.""" 43 | 44 | def to_representation(self, instance): 45 | """Render the model instance to a Python dict. 46 | 47 | If `include_inherited` is specified as a request parameter, include inherited field values as appropriate. 48 | """ 49 | req = self.context["request"] 50 | if hasattr(req, "query_params") and is_truthy(req.query_params.get("include_inherited", False)): 51 | inherited_fields = instance.get_fields(include_inherited=True) 52 | for field, data in inherited_fields.items(): 53 | setattr(instance, field, data["value"]) 54 | return super().to_representation(instance) 55 | 56 | 57 | class ExtraAttributesSerializerMixin(serializers.Serializer): # pylint: disable=abstract-method 58 | """Common mixin for BGP Extra Attributes.""" 59 | 60 | extra_attributes = serializers.JSONField(required=False, allow_null=True) 61 | 62 | def to_representation(self, instance): 63 | """Render the model instance to a Python dict. 64 | 65 | If `include_inherited` is specified as a request parameter, include object's get_extra_attributes(). 66 | """ 67 | req = self.context["request"] 68 | if hasattr(req, "query_params") and is_truthy(req.query_params.get("include_inherited", False)): 69 | setattr(instance, "extra_attributes", instance.get_extra_attributes()) 70 | return super().to_representation(instance) 71 | 72 | 73 | class PeerGroupTemplateSerializer(NautobotModelSerializer, ExtraAttributesSerializerMixin): 74 | """REST API serializer for PeerGroup records.""" 75 | 76 | class Meta: 77 | model = models.PeerGroupTemplate 78 | fields = "__all__" 79 | 80 | 81 | class PeerGroupSerializer( 82 | InheritableFieldsSerializerMixin, 83 | TaggedModelSerializerMixin, 84 | NautobotModelSerializer, 85 | ExtraAttributesSerializerMixin, 86 | ): 87 | """REST API serializer for PeerGroup records.""" 88 | 89 | class Meta: 90 | model = models.PeerGroup 91 | fields = "__all__" 92 | validators = [] 93 | 94 | def validate(self, data): 95 | """Custom validation logic to handle unique-together with a nullable field.""" 96 | if data.get("vrf"): 97 | validator = validators.UniqueTogetherValidator( 98 | queryset=models.PeerGroup.objects.all(), fields=("routing_instance", "name", "vrf") 99 | ) 100 | validator(data, self) 101 | 102 | super().validate(data) 103 | return data 104 | 105 | 106 | class PeerEndpointSerializer( 107 | InheritableFieldsSerializerMixin, 108 | TaggedModelSerializerMixin, 109 | NautobotModelSerializer, 110 | ExtraAttributesSerializerMixin, 111 | ): 112 | """REST API serializer for PeerEndpoint records.""" 113 | 114 | class Meta: 115 | model = models.PeerEndpoint 116 | fields = "__all__" 117 | 118 | def create(self, validated_data): 119 | """Create a new PeerEndpoint and update the peer on both sides.""" 120 | record = super().create(validated_data) 121 | record.peering.update_peers() 122 | return record 123 | 124 | def update(self, instance, validated_data): 125 | """When updating an existing PeerEndpoint, ensure peer is properly setup on both side.""" 126 | peering_has_been_updated = False 127 | if instance.peering.pk != validated_data.get("peering"): 128 | peering_has_been_updated = True 129 | 130 | result = super().update(instance, validated_data) 131 | 132 | if peering_has_been_updated: 133 | result.peering.update_peers() 134 | 135 | return result 136 | 137 | 138 | class BGPRoutingInstanceSerializer( 139 | NautobotModelSerializer, 140 | TaggedModelSerializerMixin, 141 | ExtraAttributesSerializerMixin, 142 | ): 143 | """REST API serializer for Peering records.""" 144 | 145 | class Meta: 146 | model = models.BGPRoutingInstance 147 | fields = "__all__" 148 | 149 | 150 | class PeeringSerializer(NautobotModelSerializer): 151 | """REST API serializer for Peering records.""" 152 | 153 | class Meta: 154 | model = models.Peering 155 | fields = "__all__" 156 | 157 | 158 | class AddressFamilySerializer(NautobotModelSerializer, ExtraAttributesSerializerMixin): 159 | """REST API serializer for AddressFamily records.""" 160 | 161 | class Meta: 162 | model = models.AddressFamily 163 | fields = "__all__" 164 | 165 | 166 | class PeerGroupAddressFamilySerializer(NautobotModelSerializer, ExtraAttributesSerializerMixin): 167 | """REST API serializer for PeerGroupAddressFamily records.""" 168 | 169 | url = serializers.HyperlinkedIdentityField( 170 | view_name="plugins-api:nautobot_bgp_models-api:peergroupaddressfamily-detail" 171 | ) 172 | 173 | class Meta: 174 | model = models.PeerGroupAddressFamily 175 | fields = "__all__" 176 | 177 | 178 | class PeerEndpointAddressFamilySerializer(NautobotModelSerializer, ExtraAttributesSerializerMixin): 179 | """REST API serializer for PeerEndpointAddressFamily records.""" 180 | 181 | url = serializers.HyperlinkedIdentityField( 182 | view_name="plugins-api:nautobot_bgp_models-api:peerendpointaddressfamily-detail" 183 | ) 184 | 185 | class Meta: 186 | model = models.PeerEndpointAddressFamily 187 | fields = "__all__" 188 | -------------------------------------------------------------------------------- /nautobot_bgp_models/api/urls.py: -------------------------------------------------------------------------------- 1 | """REST API URL registration for nautobot_bgp_models.""" 2 | 3 | from nautobot.apps.api import OrderedDefaultRouter 4 | 5 | from . import views 6 | 7 | # Use instead of rest_framework.routers.DefaultRouter so that we get bulk-update/bulk-delete features 8 | router = OrderedDefaultRouter() 9 | 10 | router.register("autonomous-systems", views.AutonomousSystemViewSet) 11 | router.register("autonomous-system-ranges", views.AutonomousSystemRangeViewSet) 12 | router.register("peer-groups", views.PeerGroupViewSet) 13 | router.register("peer-group-templates", views.PeerGroupTemplateViewSet) 14 | router.register("peer-endpoints", views.PeerEndpointViewSet) 15 | router.register("peerings", views.PeeringViewSet) 16 | router.register("address-families", views.AddressFamilyViewSet) 17 | router.register("peer-group-address-families", views.PeerGroupAddressFamilyViewSet) 18 | router.register("peer-endpoint-address-families", views.PeerEndpointAddressFamilyViewSet) 19 | router.register("routing-instances", views.BGPRoutingInstanceViewSet) 20 | 21 | app_name = "nautobot_bgp_models-api" 22 | urlpatterns = router.urls 23 | -------------------------------------------------------------------------------- /nautobot_bgp_models/api/views.py: -------------------------------------------------------------------------------- 1 | """REST API viewsets for nautobot_bgp_models.""" 2 | 3 | from drf_spectacular.types import OpenApiTypes 4 | from drf_spectacular.utils import OpenApiParameter, extend_schema 5 | from nautobot.apps.api import NautobotModelViewSet 6 | from rest_framework.filters import OrderingFilter 7 | 8 | from nautobot_bgp_models import filters, models 9 | from nautobot_bgp_models.api.filter_backends import IncludeInheritedFilterBackend 10 | 11 | from . import serializers 12 | 13 | 14 | class BGPRoutingInstanceViewSet(NautobotModelViewSet): 15 | """REST API viewset for BGPRoutingInstance records.""" 16 | 17 | queryset = models.BGPRoutingInstance.objects.all() 18 | serializer_class = serializers.BGPRoutingInstanceSerializer 19 | filterset_class = filters.BGPRoutingInstanceFilterSet 20 | 21 | 22 | class AutonomousSystemViewSet(NautobotModelViewSet): 23 | """REST API viewset for AutonomousSystem records.""" 24 | 25 | queryset = models.AutonomousSystem.objects.all() 26 | serializer_class = serializers.AutonomousSystemSerializer 27 | filterset_class = filters.AutonomousSystemFilterSet 28 | 29 | 30 | class AutonomousSystemRangeViewSet(NautobotModelViewSet): 31 | """REST API viewset for AutonomousSystemRange records.""" 32 | 33 | queryset = models.AutonomousSystemRange.objects.all() 34 | serializer_class = serializers.AutonomousSystemRangeSerializer 35 | filterset_class = filters.AutonomousSystemRangeFilterSet 36 | 37 | 38 | include_inherited = OpenApiParameter( 39 | name="include_inherited", 40 | required=False, 41 | location=OpenApiParameter.QUERY, 42 | description="Include inherited configuration values", 43 | type=OpenApiTypes.BOOL, 44 | ) 45 | 46 | 47 | class InheritableFieldsViewSetMixin: 48 | """Common mixin for ViewSets that support an additional `include_inherited` query parameter.""" 49 | 50 | @extend_schema(parameters=[include_inherited]) 51 | def list(self, request): 52 | """List all objects of this type.""" 53 | return super().list(request) 54 | 55 | @extend_schema(parameters=[include_inherited]) 56 | def retrieve(self, request, pk=None): 57 | """Retrieve a specific object instance.""" 58 | return super().retrieve(request, pk=pk) 59 | 60 | 61 | class PeerGroupViewSet(InheritableFieldsViewSetMixin, NautobotModelViewSet): 62 | """REST API viewset for PeerGroup records.""" 63 | 64 | queryset = models.PeerGroup.objects.all() 65 | serializer_class = serializers.PeerGroupSerializer 66 | filter_backends = [IncludeInheritedFilterBackend, OrderingFilter] 67 | filterset_class = filters.PeerGroupFilterSet 68 | 69 | 70 | class PeerGroupTemplateViewSet(InheritableFieldsViewSetMixin, NautobotModelViewSet): 71 | """REST API viewset for PeerGroupTemplate records.""" 72 | 73 | queryset = models.PeerGroupTemplate.objects.all() 74 | serializer_class = serializers.PeerGroupTemplateSerializer 75 | filterset_class = filters.PeerGroupTemplateFilterSet 76 | 77 | 78 | class PeerEndpointViewSet(InheritableFieldsViewSetMixin, NautobotModelViewSet): 79 | """REST API viewset for PeerEndpoint records.""" 80 | 81 | queryset = models.PeerEndpoint.objects.all() 82 | serializer_class = serializers.PeerEndpointSerializer 83 | filter_backends = [IncludeInheritedFilterBackend, OrderingFilter] 84 | filterset_class = filters.PeerEndpointFilterSet 85 | 86 | 87 | class PeeringViewSet(NautobotModelViewSet): 88 | """REST API viewset for Peering records.""" 89 | 90 | queryset = models.Peering.objects.all() 91 | serializer_class = serializers.PeeringSerializer 92 | filterset_class = filters.PeeringFilterSet 93 | 94 | 95 | class AddressFamilyViewSet(InheritableFieldsViewSetMixin, NautobotModelViewSet): 96 | """REST API viewset for AddressFamily records.""" 97 | 98 | queryset = models.AddressFamily.objects.all() 99 | serializer_class = serializers.AddressFamilySerializer 100 | filterset_class = filters.AddressFamilyFilterSet 101 | 102 | 103 | class PeerGroupAddressFamilyViewSet(InheritableFieldsViewSetMixin, NautobotModelViewSet): 104 | """REST API viewset for PeerGroupAddressFamily records.""" 105 | 106 | queryset = models.PeerGroupAddressFamily.objects.all() 107 | serializer_class = serializers.PeerGroupAddressFamilySerializer 108 | filterset_class = filters.PeerGroupAddressFamilyFilterSet 109 | 110 | 111 | class PeerEndpointAddressFamilyViewSet(InheritableFieldsViewSetMixin, NautobotModelViewSet): 112 | """REST API viewset for PeerEndpointAddressFamily records.""" 113 | 114 | queryset = models.PeerEndpointAddressFamily.objects.all() 115 | serializer_class = serializers.PeerEndpointAddressFamilySerializer 116 | filterset_class = filters.PeerEndpointAddressFamilyFilterSet 117 | -------------------------------------------------------------------------------- /nautobot_bgp_models/app-config-schema.json: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /nautobot_bgp_models/choices.py: -------------------------------------------------------------------------------- 1 | """ChoiceSet definitions for nautobot_bgp_models.""" 2 | 3 | from nautobot.apps.choices import ChoiceSet 4 | 5 | 6 | class AFISAFIChoices(ChoiceSet): 7 | """Choices for the "afi_safi" field on the AddressFamily model.""" 8 | 9 | AFI_IPV4_UNICAST = "ipv4_unicast" 10 | AFI_IPV4_LABELED_UNICAST = "ipv4_labeled_unicast" 11 | AFI_IPV4_MULTICAST = "ipv4_multicast" 12 | 13 | AFI_IPV6_UNICAST = "ipv6_unicast" 14 | AFI_IPV6_LABELED_UNICAST = "ipv6_labeled_unicast" 15 | AFI_IPV6_MULTICAST = "ipv6_multicast" 16 | 17 | AFI_IPV4_FLOWSPEC = "ipv4_flowspec" 18 | AFI_IPV6_FLOWSPEC = "ipv6_flowspec" 19 | 20 | AFI_VPNV4_UNICAST = "vpnv4_unicast" 21 | AFI_VPNV4_MULTICAST = "vpnv4_multicast" 22 | 23 | AFI_VPNV6_UNICAST = "vpnv6_unicast" 24 | AFI_VPNV6_MULTICAST = "vpnv6_multicast" 25 | 26 | AFI_L2_EVPN = "l2_evpn" 27 | AFI_L2_VPLS = "l2_vpls" 28 | 29 | CHOICES = ( 30 | (AFI_IPV4_UNICAST, "IPv4 Unicast"), 31 | (AFI_IPV4_LABELED_UNICAST, "IPv4 Labeled Unicast"), 32 | (AFI_IPV4_MULTICAST, "IPv4 Multicast"), 33 | (AFI_IPV4_FLOWSPEC, "IPv4 Flowspec"), 34 | (AFI_IPV6_UNICAST, "IPv6 Unicast"), 35 | (AFI_IPV6_LABELED_UNICAST, "IPv6 Labeled Unicast"), 36 | (AFI_IPV6_MULTICAST, "IPv6 Multicast"), 37 | (AFI_IPV6_FLOWSPEC, "IPv6 Flowspec"), 38 | (AFI_VPNV4_UNICAST, "VPNv4 Unicast"), 39 | (AFI_VPNV4_MULTICAST, "VPNv4 Multicast"), 40 | (AFI_VPNV6_UNICAST, "VPNv6 Unicast"), 41 | (AFI_VPNV6_MULTICAST, "VPNv6 Multicast"), 42 | (AFI_L2_EVPN, "L2 EVPN"), 43 | (AFI_L2_VPLS, "L2 VPLS"), 44 | ) 45 | -------------------------------------------------------------------------------- /nautobot_bgp_models/dolt_compat.py: -------------------------------------------------------------------------------- 1 | """Dolt compatibility registration.""" 2 | 3 | from . import tables 4 | 5 | try: 6 | import dolt # noqa pylint: disable=unused-import 7 | except ImportError: 8 | DOLT_AVAILABLE = False 9 | else: 10 | DOLT_AVAILABLE = True 11 | 12 | 13 | # Only do this if Dolt is available (aka "version control"). 14 | if DOLT_AVAILABLE: 15 | # Tables used to display diffs 16 | dolt.register_diff_tables( 17 | { 18 | "nautobot_bgp_models": { 19 | "bgproutinginstance": tables.BGPRoutingInstanceTable, 20 | "autonomoussystem": tables.AutonomousSystemTable, 21 | "peergroup": tables.PeerGroupTable, 22 | "peergroupaddressfamily": tables.PeerGroupAddressFamilyTable, 23 | "peergrouptemplate": tables.PeerGroupTemplateTable, 24 | "peerendpoint": tables.PeerEndpointTable, 25 | "peerendpointaddressfamily": tables.PeerEndpointAddressFamilyTable, 26 | "peering": tables.PeeringTable, 27 | "addressfamily": tables.AddressFamilyTable, 28 | } 29 | } 30 | ) 31 | 32 | # Register all models w/ Dolt. 33 | dolt.register_versioned_models({"nautobot_bgp_models": True}) 34 | -------------------------------------------------------------------------------- /nautobot_bgp_models/filter_extensions.py: -------------------------------------------------------------------------------- 1 | """Filter Extensions definitions for nautobot_bgp_models.""" 2 | 3 | from django import forms 4 | from nautobot.apps.filters import FilterExtension, MultiValueCharFilter 5 | 6 | 7 | class IPAddressFilterExtension(FilterExtension): 8 | """IP Address Filter Extension.""" 9 | 10 | model = "ipam.ipaddress" 11 | 12 | filterset_fields = { 13 | "nautobot_bgp_models_ips_bgp_routing_instance": MultiValueCharFilter( 14 | field_name="interfaces__device__bgp_routing_instances__id", 15 | label="Routing Instance UUID", 16 | ), 17 | } 18 | 19 | filterform_fields = { 20 | "nautobot_bgp_models_ips_bgp_routing_instance": forms.CharField(required=False, label="Routing Instance UUID"), 21 | } 22 | 23 | 24 | class InterfaceFilterExtension(FilterExtension): 25 | """Interface filter extension.""" 26 | 27 | model = "dcim.interface" 28 | 29 | filterset_fields = { 30 | "nautobot_bgp_models_interfaces_bgp_routing_instance": MultiValueCharFilter( 31 | field_name="device__bgp_routing_instances__id", 32 | label="Routing Instance UUID", 33 | ), 34 | } 35 | 36 | filterform_fields = { 37 | "nautobot_bgp_models_interfaces_bgp_routing_instance": forms.CharField( 38 | required=False, label="Routing Instance UUID" 39 | ), 40 | } 41 | 42 | 43 | filter_extensions = [InterfaceFilterExtension, IPAddressFilterExtension] 44 | -------------------------------------------------------------------------------- /nautobot_bgp_models/helpers.py: -------------------------------------------------------------------------------- 1 | """BGP helper functions.""" 2 | 3 | 4 | def add_available_asns(instance, asns): 5 | """Create fake records for all gaps between used Autonomous Systems.""" 6 | new_list = [] 7 | last_asn = None 8 | for asn_val in asns: 9 | if asn_val.asn == instance.asn_min: 10 | new_list.append(asn_val) 11 | last_asn = asn_val.asn 12 | elif not last_asn: 13 | new_list.append({"asn": instance.asn_min, "available": asn_val.asn - instance.asn_min}) 14 | new_list.append(asn_val) 15 | last_asn = asn_val.asn 16 | elif instance.asn_min <= asn_val.asn <= instance.asn_max: 17 | if asn_val.asn - last_asn > 1: 18 | new_list.append({"asn": last_asn + 1, "available": asn_val.asn - last_asn - 1}) 19 | new_list.append(asn_val) 20 | last_asn = asn_val.asn 21 | elif asn_val.asn == instance.asn_max: 22 | new_list.append(asn_val) 23 | last_asn = asn_val.asn 24 | 25 | if not asns: 26 | new_list.append({"asn": instance.asn_min, "available": instance.asn_max - instance.asn_min + 1}) 27 | elif last_asn < instance.asn_max: 28 | new_list.append({"asn": last_asn + 1, "available": instance.asn_max - last_asn}) 29 | 30 | return new_list 31 | -------------------------------------------------------------------------------- /nautobot_bgp_models/migrations/0002_viewsets_migration.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=missing-module-docstring,missing-function-docstring,missing-class-docstring,invalid-name 2 | # Generated by Django 3.2.18 on 2023-03-15 21:03 3 | 4 | import django.db.models.deletion 5 | import nautobot.extras.models.statuses 6 | from django.db import migrations 7 | 8 | 9 | class Migration(migrations.Migration): 10 | dependencies = [ 11 | ("nautobot_bgp_models", "0001_initial"), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name="peergroup", 17 | old_name="template", 18 | new_name="peergroup_template", 19 | ), 20 | migrations.AddField( 21 | model_name="bgproutinginstance", 22 | name="status", 23 | field=nautobot.extras.models.statuses.StatusField( 24 | null=True, 25 | on_delete=django.db.models.deletion.PROTECT, 26 | related_name="nautobot_bgp_models_bgproutinginstance_related", 27 | to="extras.status", 28 | ), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /nautobot_bgp_models/migrations/0003_peergroupaddressfamily_peerendpointaddressfamily.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=missing-module-docstring,missing-function-docstring,missing-class-docstring,invalid-name 2 | # Generated by Django 3.2.16 on 2023-09-18 19:03 3 | import uuid 4 | 5 | import django.core.serializers.json 6 | import django.db.models.deletion 7 | import nautobot.extras.models.mixins 8 | from django.db import migrations, models 9 | 10 | 11 | class Migration(migrations.Migration): 12 | dependencies = [ 13 | ("ipam", "0008_prefix_vlan_vlangroup_location"), 14 | ("nautobot_bgp_models", "0002_viewsets_migration"), 15 | ] 16 | 17 | operations = [ 18 | migrations.RemoveField( 19 | model_name="addressfamily", 20 | name="export_policy", 21 | ), 22 | migrations.RemoveField( 23 | model_name="addressfamily", 24 | name="import_policy", 25 | ), 26 | migrations.RemoveField( 27 | model_name="addressfamily", 28 | name="multipath", 29 | ), 30 | migrations.RemoveField( 31 | model_name="peerendpoint", 32 | name="export_policy", 33 | ), 34 | migrations.RemoveField( 35 | model_name="peerendpoint", 36 | name="import_policy", 37 | ), 38 | migrations.RemoveField( 39 | model_name="peergroup", 40 | name="export_policy", 41 | ), 42 | migrations.RemoveField( 43 | model_name="peergroup", 44 | name="import_policy", 45 | ), 46 | migrations.RemoveField( 47 | model_name="peergrouptemplate", 48 | name="export_policy", 49 | ), 50 | migrations.RemoveField( 51 | model_name="peergrouptemplate", 52 | name="import_policy", 53 | ), 54 | migrations.AddField( 55 | model_name="addressfamily", 56 | name="extra_attributes", 57 | field=models.JSONField(blank=True, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True), 58 | ), 59 | migrations.AddField( 60 | model_name="peergroup", 61 | name="vrf", 62 | field=models.ForeignKey( 63 | blank=True, 64 | null=True, 65 | on_delete=django.db.models.deletion.PROTECT, 66 | related_name="peer_groups", 67 | to="ipam.vrf", 68 | ), 69 | ), 70 | migrations.AlterField( 71 | model_name="addressfamily", 72 | name="vrf", 73 | field=models.ForeignKey( 74 | blank=True, 75 | null=True, 76 | on_delete=django.db.models.deletion.PROTECT, 77 | related_name="address_families", 78 | to="ipam.vrf", 79 | ), 80 | ), 81 | migrations.AlterUniqueTogether( 82 | name="peergroup", 83 | unique_together={("name", "routing_instance", "vrf")}, 84 | ), 85 | migrations.CreateModel( 86 | name="PeerGroupAddressFamily", 87 | fields=[ 88 | ( 89 | "id", 90 | models.UUIDField( 91 | default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True 92 | ), 93 | ), 94 | ("created", models.DateField(auto_now_add=True, null=True)), 95 | ("last_updated", models.DateTimeField(auto_now=True, null=True)), 96 | ( 97 | "_custom_field_data", 98 | models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), 99 | ), 100 | ( 101 | "extra_attributes", 102 | models.JSONField(blank=True, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True), 103 | ), 104 | ("afi_safi", models.CharField(max_length=64)), 105 | ("import_policy", models.CharField(blank=True, default="", max_length=100)), 106 | ("export_policy", models.CharField(blank=True, default="", max_length=100)), 107 | ("multipath", models.BooleanField(blank=True, null=True)), 108 | ( 109 | "peer_group", 110 | models.ForeignKey( 111 | on_delete=django.db.models.deletion.CASCADE, 112 | related_name="address_families", 113 | to="nautobot_bgp_models.peergroup", 114 | ), 115 | ), 116 | ], 117 | options={ 118 | "verbose_name": "BGP peer-group address family", 119 | "verbose_name_plural": "BGP Peer-Group Address Families", 120 | "ordering": ["-peer_group"], 121 | "unique_together": {("peer_group", "afi_safi")}, 122 | }, 123 | bases=( 124 | models.Model, 125 | nautobot.extras.models.mixins.DynamicGroupMixin, 126 | nautobot.extras.models.mixins.NotesMixin, 127 | ), 128 | ), 129 | migrations.CreateModel( 130 | name="PeerEndpointAddressFamily", 131 | fields=[ 132 | ( 133 | "id", 134 | models.UUIDField( 135 | default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True 136 | ), 137 | ), 138 | ("created", models.DateField(auto_now_add=True, null=True)), 139 | ("last_updated", models.DateTimeField(auto_now=True, null=True)), 140 | ( 141 | "_custom_field_data", 142 | models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), 143 | ), 144 | ( 145 | "extra_attributes", 146 | models.JSONField(blank=True, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True), 147 | ), 148 | ("afi_safi", models.CharField(max_length=64)), 149 | ("import_policy", models.CharField(blank=True, default="", max_length=100)), 150 | ("export_policy", models.CharField(blank=True, default="", max_length=100)), 151 | ("multipath", models.BooleanField(blank=True, null=True)), 152 | ( 153 | "peer_endpoint", 154 | models.ForeignKey( 155 | on_delete=django.db.models.deletion.CASCADE, 156 | related_name="address_families", 157 | to="nautobot_bgp_models.peerendpoint", 158 | ), 159 | ), 160 | ], 161 | options={ 162 | "verbose_name": "BGP peer-endpoint address family", 163 | "verbose_name_plural": "BGP Peer-Endpoint Address Families", 164 | "ordering": ["-peer_endpoint"], 165 | "unique_together": {("peer_endpoint", "afi_safi")}, 166 | }, 167 | bases=( 168 | models.Model, 169 | nautobot.extras.models.mixins.DynamicGroupMixin, 170 | nautobot.extras.models.mixins.NotesMixin, 171 | ), 172 | ), 173 | ] 174 | -------------------------------------------------------------------------------- /nautobot_bgp_models/migrations/0004_use_upstream_role_part1.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=missing-module-docstring,missing-function-docstring,missing-class-docstring,invalid-name 2 | # Generated by Django 3.2.17 on 2023-02-12 19:21 3 | 4 | import django.db.models.deletion 5 | import nautobot.extras.models 6 | from django.db import migrations 7 | 8 | 9 | class Migration(migrations.Migration): 10 | dependencies = [ 11 | ("nautobot_bgp_models", "0003_peergroupaddressfamily_peerendpointaddressfamily"), 12 | ("extras", "0061_role_and_alter_status"), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name="peergroup", 18 | name="role_new", 19 | field=nautobot.extras.models.RoleField( 20 | blank=True, 21 | null=True, 22 | on_delete=django.db.models.deletion.PROTECT, 23 | related_name="nautobot_bgp_models_peergroup_related", 24 | to="extras.role", 25 | ), 26 | ), 27 | migrations.AddField( 28 | model_name="peergrouptemplate", 29 | name="role_new", 30 | field=nautobot.extras.models.RoleField( 31 | blank=True, 32 | null=True, 33 | on_delete=django.db.models.deletion.PROTECT, 34 | related_name="nautobot_bgp_models_peergrouptemplate_related", 35 | to="extras.role", 36 | ), 37 | ), 38 | migrations.AddField( 39 | model_name="peerendpoint", 40 | name="role_new", 41 | field=nautobot.extras.models.RoleField( 42 | blank=True, 43 | null=True, 44 | on_delete=django.db.models.deletion.PROTECT, 45 | related_name="nautobot_bgp_models_peerendpoint_related", 46 | to="extras.role", 47 | ), 48 | ), 49 | ] 50 | -------------------------------------------------------------------------------- /nautobot_bgp_models/migrations/0005_use_upstream_role_part2.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=missing-module-docstring,missing-function-docstring,missing-class-docstring,invalid-name 2 | # Generated by Django 3.2.17 on 2023-02-12 19:21 3 | 4 | from django.db import migrations 5 | from nautobot.core.utils.migrations import update_object_change_ct_for_replaced_models 6 | 7 | 8 | def create_roles(apps, schema_editor): # pylint: disable=unused-argument 9 | ContentType = apps.get_model("contenttypes.ContentType") # pylint: disable=invalid-name 10 | PeeringRole = apps.get_model("nautobot_bgp_models.PeeringRole") # pylint: disable=invalid-name 11 | NautobotRole = apps.get_model("extras.Role") # pylint: disable=invalid-name 12 | PeerGroup = apps.get_model("nautobot_bgp_models.PeerGroup") # pylint: disable=invalid-name 13 | PeerGroupTemplate = apps.get_model("nautobot_bgp_models.PeerGroupTemplate") # pylint: disable=invalid-name 14 | PeerEndpoint = apps.get_model("nautobot_bgp_models.PeerEndpoint") # pylint: disable=invalid-name 15 | pg_ct = ContentType.objects.get_for_model(PeerGroup) 16 | pgt_ct = ContentType.objects.get_for_model(PeerGroupTemplate) 17 | pe_ct = ContentType.objects.get_for_model(PeerEndpoint) 18 | for role in PeeringRole.objects.all(): 19 | nb_role, _ = NautobotRole.objects.get_or_create( 20 | name=role.name, 21 | color=role.color, 22 | description=role.description, 23 | ) 24 | nb_role.content_types.add(pg_ct) 25 | nb_role.content_types.add(pgt_ct) 26 | nb_role.content_types.add(pe_ct) 27 | for group in PeerGroup.objects.exclude(role__isnull=True).exclude(role__name__exact=""): 28 | group.role_new = NautobotRole.objects.get(name=group.role.name) 29 | group.save() 30 | for group in PeerGroupTemplate.objects.exclude(role__isnull=True).exclude(role__name__exact=""): 31 | group.role_new = NautobotRole.objects.get(name=group.role.name) 32 | group.save() 33 | for peer in PeerEndpoint.objects.exclude(role__isnull=True).exclude(role__name__exact=""): 34 | peer.role_new = NautobotRole.objects.get(name=peer.role.name) 35 | peer.save() 36 | 37 | 38 | def reverse_create_roles(apps, schema_editor): # pylint: disable=unused-argument 39 | PeeringRole = apps.get_model("nautobot_bgp_models.PeeringRole") # pylint: disable=invalid-name 40 | PeerGroup = apps.get_model("nautobot_bgp_models.PeerGroup") # pylint: disable=invalid-name 41 | PeerGroupTemplate = apps.get_model("nautobot_bgp_models.PeerGroupTemplate") # pylint: disable=invalid-name 42 | PeerEndpoint = apps.get_model("nautobot_bgp_models.PeerEndpoint") # pylint: disable=invalid-name 43 | for group in PeerGroup.objects.exclude(role_new__isnull=True).exclude(role_new__name__exact=""): 44 | group.role, _ = PeeringRole.objects.get_or_create( 45 | name=group.role_new.name, 46 | # slug=group.role_new.slug, 47 | color=group.role_new.color, 48 | description=group.role_new.description, 49 | ) 50 | group.save() 51 | for group in PeerGroupTemplate.objects.exclude(role_new__isnull=True).exclude(role_new__name__exact=""): 52 | group.role, _ = PeeringRole.objects.get_or_create( 53 | name=group.role_new.name, 54 | # slug=group.role_new.slug, 55 | color=group.role_new.color, 56 | description=group.role_new.description, 57 | ) 58 | group.save() 59 | for peer in PeerEndpoint.objects.exclude(role_new__isnull=True).exclude(role_new__name__exact=""): 60 | peer.role, _ = PeeringRole.objects.get_or_create( 61 | name=peer.role_new.name, 62 | # slug=peer.role_new.slug, 63 | color=peer.role_new.color, 64 | description=peer.role_new.description, 65 | ) 66 | peer.save() 67 | 68 | 69 | def update_object_change_ct_for_replaced_role(apps, schema): # pylint: disable=unused-argument 70 | """Transfer data from legacy_role to new_role.""" 71 | 72 | update_object_change_ct_for_replaced_models( 73 | apps=apps, 74 | new_app_model={"app_name": "extras", "model": "role"}, 75 | replaced_apps_models=[ 76 | {"app_name": "nautobot_bgp_models", "model": "peeringrole"}, 77 | ], 78 | ) 79 | 80 | 81 | def reverse_update_object_change_ct_for_replaced_role(apps, schema): # pylint: disable=unused-argument 82 | """Transfer data from new_role to legacy_role.""" 83 | 84 | update_object_change_ct_for_replaced_models( 85 | apps=apps, 86 | new_app_model={"app_name": "extras", "model": "role"}, 87 | replaced_apps_models=[ 88 | {"app_name": "nautobot_bgp_models", "model": "peeringrole"}, 89 | ], 90 | reverse_migration=True, 91 | ) 92 | 93 | 94 | class Migration(migrations.Migration): 95 | dependencies = [ 96 | ("nautobot_bgp_models", "0004_use_upstream_role_part1"), 97 | ] 98 | 99 | operations = [ 100 | migrations.RunPython(code=create_roles, reverse_code=reverse_create_roles), 101 | migrations.RunPython( 102 | code=update_object_change_ct_for_replaced_role, 103 | reverse_code=reverse_update_object_change_ct_for_replaced_role, 104 | ), 105 | ] 106 | -------------------------------------------------------------------------------- /nautobot_bgp_models/migrations/0006_use_upstream_role_part3.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=missing-module-docstring,missing-function-docstring,missing-class-docstring,invalid-name 2 | # Generated by Django 3.2.17 on 2023-02-12 20:25 3 | 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | ("nautobot_bgp_models", "0005_use_upstream_role_part2"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name="peerendpoint", 15 | name="role", 16 | ), 17 | migrations.RemoveField( 18 | model_name="peergroup", 19 | name="role", 20 | ), 21 | migrations.RemoveField( 22 | model_name="peergrouptemplate", 23 | name="role", 24 | ), 25 | migrations.DeleteModel( 26 | name="PeeringRole", 27 | ), 28 | migrations.RenameField( 29 | model_name="peergroup", 30 | old_name="role_new", 31 | new_name="role", 32 | ), 33 | migrations.RenameField( 34 | model_name="peergrouptemplate", 35 | old_name="role_new", 36 | new_name="role", 37 | ), 38 | migrations.RenameField( 39 | model_name="peerendpoint", 40 | old_name="role_new", 41 | new_name="role", 42 | ), 43 | ] 44 | -------------------------------------------------------------------------------- /nautobot_bgp_models/migrations/0007_use_upstream_role_part4.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=missing-module-docstring,missing-function-docstring,missing-class-docstring,invalid-name 2 | 3 | import django.db.models.deletion 4 | import nautobot.extras.models 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | dependencies = [ 10 | ("nautobot_bgp_models", "0006_use_upstream_role_part3"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="peerendpoint", 16 | name="role", 17 | field=nautobot.extras.models.roles.RoleField( 18 | blank=True, 19 | null=True, 20 | on_delete=django.db.models.deletion.PROTECT, 21 | related_name="bgp_peer_endpoints", 22 | to="extras.role", 23 | ), 24 | ), 25 | migrations.AlterField( 26 | model_name="peergroup", 27 | name="role", 28 | field=nautobot.extras.models.roles.RoleField( 29 | blank=True, 30 | null=True, 31 | on_delete=django.db.models.deletion.PROTECT, 32 | related_name="bgp_peer_groups", 33 | to="extras.role", 34 | ), 35 | ), 36 | migrations.AlterField( 37 | model_name="peergrouptemplate", 38 | name="role", 39 | field=nautobot.extras.models.roles.RoleField( 40 | blank=True, 41 | null=True, 42 | on_delete=django.db.models.deletion.PROTECT, 43 | related_name="bgp_peer_group_templates", 44 | to="extras.role", 45 | ), 46 | ), 47 | ] 48 | -------------------------------------------------------------------------------- /nautobot_bgp_models/migrations/0008_nautobotv2_updates.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=missing-module-docstring,missing-function-docstring,missing-class-docstring,invalid-name 2 | 3 | import django.db.models.deletion 4 | import nautobot.core.models.fields 5 | import nautobot.extras.models.roles 6 | import nautobot.extras.models.statuses 7 | from django.db import migrations, models 8 | 9 | 10 | class Migration(migrations.Migration): 11 | dependencies = [ 12 | ("nautobot_bgp_models", "0007_use_upstream_role_part4"), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name="addressfamily", 18 | name="created", 19 | field=models.DateTimeField(auto_now_add=True, null=True), 20 | ), 21 | migrations.AlterField( 22 | model_name="autonomoussystem", 23 | name="created", 24 | field=models.DateTimeField(auto_now_add=True, null=True), 25 | ), 26 | migrations.AlterField( 27 | model_name="autonomoussystem", 28 | name="status", 29 | field=nautobot.extras.models.statuses.StatusField( 30 | null=True, 31 | on_delete=django.db.models.deletion.PROTECT, 32 | related_name="autonomous_systems", 33 | to="extras.status", 34 | ), 35 | ), 36 | migrations.AlterField( 37 | model_name="autonomoussystem", 38 | name="tags", 39 | field=nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag"), 40 | ), 41 | migrations.AlterField( 42 | model_name="bgproutinginstance", 43 | name="created", 44 | field=models.DateTimeField(auto_now_add=True, null=True), 45 | ), 46 | migrations.AlterField( 47 | model_name="bgproutinginstance", 48 | name="status", 49 | field=nautobot.extras.models.statuses.StatusField( 50 | null=True, 51 | on_delete=django.db.models.deletion.PROTECT, 52 | related_name="bgp_routing_instances", 53 | to="extras.status", 54 | ), 55 | ), 56 | migrations.AlterField( 57 | model_name="bgproutinginstance", 58 | name="tags", 59 | field=nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag"), 60 | ), 61 | migrations.AlterField( 62 | model_name="peerendpoint", 63 | name="created", 64 | field=models.DateTimeField(auto_now_add=True, null=True), 65 | ), 66 | migrations.AlterField( 67 | model_name="peerendpoint", 68 | name="tags", 69 | field=nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag"), 70 | ), 71 | migrations.AlterField( 72 | model_name="peergroup", 73 | name="created", 74 | field=models.DateTimeField(auto_now_add=True, null=True), 75 | ), 76 | migrations.AlterField( 77 | model_name="peergroup", 78 | name="tags", 79 | field=nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag"), 80 | ), 81 | migrations.AlterField( 82 | model_name="peergrouptemplate", 83 | name="created", 84 | field=models.DateTimeField(auto_now_add=True, null=True), 85 | ), 86 | migrations.AlterField( 87 | model_name="peergrouptemplate", 88 | name="tags", 89 | field=nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag"), 90 | ), 91 | migrations.AlterField( 92 | model_name="peering", 93 | name="created", 94 | field=models.DateTimeField(auto_now_add=True, null=True), 95 | ), 96 | migrations.AlterField( 97 | model_name="peering", 98 | name="status", 99 | field=nautobot.extras.models.statuses.StatusField( 100 | null=True, 101 | on_delete=django.db.models.deletion.PROTECT, 102 | related_name="bgp_peerings", 103 | to="extras.status", 104 | ), 105 | ), 106 | migrations.AlterModelOptions( 107 | name="peerendpoint", 108 | options={"verbose_name": "BGP Peer Endpoint"}, 109 | ), 110 | migrations.AlterModelOptions( 111 | name="peergroup", 112 | options={"ordering": ["name"], "verbose_name": "BGP Peer Group"}, 113 | ), 114 | migrations.AlterField( 115 | model_name="peerendpointaddressfamily", 116 | name="created", 117 | field=models.DateTimeField(auto_now_add=True, null=True), 118 | ), 119 | migrations.AlterField( 120 | model_name="peergroupaddressfamily", 121 | name="created", 122 | field=models.DateTimeField(auto_now_add=True, null=True), 123 | ), 124 | ] 125 | -------------------------------------------------------------------------------- /nautobot_bgp_models/migrations/0009_autonomoussystemrange.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=missing-module-docstring,missing-function-docstring,missing-class-docstring,invalid-name 2 | 3 | import uuid 4 | 5 | import django.core.serializers.json 6 | import django.db.models.deletion 7 | import nautobot.core.models.fields 8 | import nautobot.dcim.fields 9 | import nautobot.extras.models.mixins 10 | from django.db import migrations, models 11 | 12 | 13 | class Migration(migrations.Migration): 14 | dependencies = [ 15 | ("tenancy", "0008_tagsfield"), 16 | ("extras", "0098_rename_data_jobresult_result"), 17 | ("nautobot_bgp_models", "0008_nautobotv2_updates"), 18 | ] 19 | 20 | operations = [ 21 | migrations.CreateModel( 22 | name="AutonomousSystemRange", 23 | fields=[ 24 | ( 25 | "id", 26 | models.UUIDField( 27 | default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True 28 | ), 29 | ), 30 | ("created", models.DateTimeField(auto_now_add=True, null=True)), 31 | ("last_updated", models.DateTimeField(auto_now=True, null=True)), 32 | ( 33 | "_custom_field_data", 34 | models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), 35 | ), 36 | ("name", models.CharField(max_length=255, unique=True)), 37 | ("asn_min", nautobot.dcim.fields.ASNField()), 38 | ("asn_max", nautobot.dcim.fields.ASNField()), 39 | ("description", models.CharField(blank=True, max_length=255)), 40 | ("tags", nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag")), 41 | ( 42 | "tenant", 43 | models.ForeignKey( 44 | blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to="tenancy.tenant" 45 | ), 46 | ), 47 | ], 48 | options={ 49 | "verbose_name": "Autonomous System Range", 50 | "ordering": ["asn_min"], 51 | }, 52 | bases=( 53 | models.Model, 54 | nautobot.extras.models.mixins.DynamicGroupMixin, 55 | nautobot.extras.models.mixins.NotesMixin, 56 | ), 57 | ), 58 | ] 59 | -------------------------------------------------------------------------------- /nautobot_bgp_models/migrations/0010_alter_autonomoussystem_status_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.14 on 2024-08-07 16:00 2 | 3 | import django.db.models.deletion 4 | import nautobot.extras.models.roles 5 | import nautobot.extras.models.statuses 6 | from django.db import migrations 7 | 8 | 9 | class Migration(migrations.Migration): 10 | dependencies = [ 11 | ("nautobot_bgp_models", "0009_autonomoussystemrange"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name="autonomoussystem", 17 | name="status", 18 | field=nautobot.extras.models.statuses.StatusField( 19 | null=True, on_delete=django.db.models.deletion.PROTECT, to="extras.status" 20 | ), 21 | ), 22 | migrations.AlterField( 23 | model_name="bgproutinginstance", 24 | name="status", 25 | field=nautobot.extras.models.statuses.StatusField( 26 | null=True, on_delete=django.db.models.deletion.PROTECT, to="extras.status" 27 | ), 28 | ), 29 | migrations.AlterField( 30 | model_name="peerendpoint", 31 | name="role", 32 | field=nautobot.extras.models.roles.RoleField( 33 | blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to="extras.role" 34 | ), 35 | ), 36 | migrations.AlterField( 37 | model_name="peergroup", 38 | name="role", 39 | field=nautobot.extras.models.roles.RoleField( 40 | blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to="extras.role" 41 | ), 42 | ), 43 | migrations.AlterField( 44 | model_name="peergrouptemplate", 45 | name="role", 46 | field=nautobot.extras.models.roles.RoleField( 47 | blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to="extras.role" 48 | ), 49 | ), 50 | migrations.AlterField( 51 | model_name="peering", 52 | name="status", 53 | field=nautobot.extras.models.statuses.StatusField( 54 | null=True, on_delete=django.db.models.deletion.PROTECT, to="extras.status" 55 | ), 56 | ), 57 | ] 58 | -------------------------------------------------------------------------------- /nautobot_bgp_models/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nautobot/nautobot-app-bgp-models/4afcfe81fc8caf516ab73130d139b0252a6f0801/nautobot_bgp_models/migrations/__init__.py -------------------------------------------------------------------------------- /nautobot_bgp_models/navigation.py: -------------------------------------------------------------------------------- 1 | """Nautobot UI navigation elements for nautobot_bgp_models.""" 2 | 3 | from nautobot.core.apps import NavMenuAddButton, NavMenuGroup, NavMenuImportButton, NavMenuItem, NavMenuTab 4 | 5 | menu_items = ( 6 | NavMenuTab( 7 | name="Routing", 8 | weight=350, 9 | groups=( 10 | NavMenuGroup( 11 | name="BGP - Global", 12 | weight=100, 13 | items=( 14 | NavMenuItem( 15 | link="plugins:nautobot_bgp_models:autonomoussystem_list", 16 | name="Autonomous Systems", 17 | permissions=["nautobot_bgp_models.view_autonomoussystem"], 18 | buttons=( 19 | NavMenuAddButton( 20 | link="plugins:nautobot_bgp_models:autonomoussystem_add", 21 | permissions=["nautobot_bgp_models.add_autonomoussystem"], 22 | ), 23 | NavMenuImportButton( 24 | link="plugins:nautobot_bgp_models:autonomoussystem_import", 25 | permissions=["nautobot_bgp_models.add_autonomoussystem"], 26 | ), 27 | ), 28 | ), 29 | NavMenuItem( 30 | link="plugins:nautobot_bgp_models:autonomoussystemrange_list", 31 | name="Autonomous System Ranges", 32 | permissions=["nautobot_bgp_models.view_autonomoussystemrange"], 33 | buttons=( 34 | NavMenuAddButton( 35 | link="plugins:nautobot_bgp_models:autonomoussystemrange_add", 36 | permissions=["nautobot_bgp_models.add_autonomoussystemrange"], 37 | ), 38 | NavMenuImportButton( 39 | link="plugins:nautobot_bgp_models:autonomoussystemrange_import", 40 | permissions=["nautobot_bgp_models.add_autonomoussystemrange"], 41 | ), 42 | ), 43 | ), 44 | NavMenuItem( 45 | link="plugins:nautobot_bgp_models:peergrouptemplate_list", 46 | name="Peer Group Templates", 47 | permissions=["nautobot_bgp_models.view_peergrouptemplate"], 48 | buttons=( 49 | NavMenuAddButton( 50 | link="plugins:nautobot_bgp_models:peergrouptemplate_add", 51 | permissions=["nautobot_bgp_models.add_peergrouptemplate"], 52 | ), 53 | NavMenuImportButton( 54 | link="plugins:nautobot_bgp_models:peergrouptemplate_import", 55 | permissions=["nautobot_bgp_models.add_peergrouptemplate"], 56 | ), 57 | ), 58 | ), 59 | ), 60 | ), 61 | NavMenuGroup( 62 | name="BGP - Instances", 63 | weight=100, 64 | items=( 65 | NavMenuItem( 66 | link="plugins:nautobot_bgp_models:bgproutinginstance_list", 67 | name="Routing Instances", 68 | permissions=["nautobot_bgp_models.view_bgproutinginstance"], 69 | buttons=( 70 | NavMenuAddButton( 71 | link="plugins:nautobot_bgp_models:bgproutinginstance_add", 72 | permissions=["nautobot_bgp_models.add_bgproutinginstance"], 73 | ), 74 | NavMenuImportButton( 75 | link="plugins:nautobot_bgp_models:bgproutinginstance_import", 76 | permissions=["nautobot_bgp_models.add_bgproutinginstance"], 77 | ), 78 | ), 79 | ), 80 | NavMenuItem( 81 | link="plugins:nautobot_bgp_models:addressfamily_list", 82 | name="Address-families (AFI-SAFI)", 83 | permissions=["nautobot_bgp_models.view_addressfamily"], 84 | buttons=( 85 | NavMenuAddButton( 86 | link="plugins:nautobot_bgp_models:addressfamily_add", 87 | permissions=["nautobot_bgp_models.add_addressfamily"], 88 | ), 89 | NavMenuImportButton( 90 | link="plugins:nautobot_bgp_models:addressfamily_import", 91 | permissions=["nautobot_bgp_models.add_addressfamily"], 92 | ), 93 | ), 94 | ), 95 | NavMenuItem( 96 | link="plugins:nautobot_bgp_models:peergroup_list", 97 | name="Peer Groups", 98 | permissions=["nautobot_bgp_models.view_peergroup"], 99 | buttons=( 100 | NavMenuAddButton( 101 | link="plugins:nautobot_bgp_models:peergroup_add", 102 | permissions=["nautobot_bgp_models.add_peergroup"], 103 | ), 104 | NavMenuImportButton( 105 | link="plugins:nautobot_bgp_models:peergroup_import", 106 | permissions=["nautobot_bgp_models.add_peergroup"], 107 | ), 108 | ), 109 | ), 110 | NavMenuItem( 111 | link="plugins:nautobot_bgp_models:peergroupaddressfamily_list", 112 | name="Peer Group Address-families (AFI-SAFI)", 113 | permissions=["nautobot_bgp_models.view_peergroupaddressfamily"], 114 | buttons=( 115 | NavMenuAddButton( 116 | link="plugins:nautobot_bgp_models:peergroupaddressfamily_add", 117 | permissions=["nautobot_bgp_models.add_peergroupaddressfamily"], 118 | ), 119 | NavMenuImportButton( 120 | link="plugins:nautobot_bgp_models:peergroupaddressfamily_import", 121 | permissions=["nautobot_bgp_models.add_peergroupaddressfamily"], 122 | ), 123 | ), 124 | ), 125 | ), 126 | ), 127 | NavMenuGroup( 128 | name="BGP - Peerings", 129 | weight=100, 130 | items=( 131 | NavMenuItem( 132 | link="plugins:nautobot_bgp_models:peering_list", 133 | name="Peerings", 134 | permissions=["nautobot_bgp_models.view_peering"], 135 | buttons=( 136 | NavMenuAddButton( 137 | link="plugins:nautobot_bgp_models:peering_add", 138 | permissions=["nautobot_bgp_models.add_peering"], 139 | ), 140 | ), 141 | ), 142 | NavMenuItem( 143 | link="plugins:nautobot_bgp_models:peerendpointaddressfamily_list", 144 | name="Peer Endpoint Address-families (AFI-SAFI)", 145 | permissions=["nautobot_bgp_models.view_peerendpointaddressfamily"], 146 | buttons=( 147 | NavMenuAddButton( 148 | link="plugins:nautobot_bgp_models:peerendpointaddressfamily_add", 149 | permissions=["nautobot_bgp_models.add_peerendpointaddressfamily"], 150 | ), 151 | ), 152 | ), 153 | ), 154 | ), 155 | ), 156 | ), 157 | ) 158 | -------------------------------------------------------------------------------- /nautobot_bgp_models/signals.py: -------------------------------------------------------------------------------- 1 | """Nautobot signal handler functions for nautobot_bgp_models.""" 2 | 3 | from django.apps import apps as global_apps 4 | from django.conf import settings 5 | 6 | PLUGIN_SETTINGS = settings.PLUGINS_CONFIG["nautobot_bgp_models"] 7 | 8 | 9 | def post_migrate_create_statuses(sender, *, apps=global_apps, **kwargs): 10 | """Callback function for post_migrate() -- create default Statuses.""" 11 | # pylint: disable=invalid-name 12 | if not apps: 13 | return 14 | 15 | Status = apps.get_model("extras", "Status") 16 | 17 | for model_name, default_statuses in PLUGIN_SETTINGS.get("default_statuses", {}).items(): 18 | model = sender.get_model(model_name) 19 | 20 | ContentType = apps.get_model("contenttypes", "ContentType") 21 | ct_model = ContentType.objects.get_for_model(model) 22 | for name in default_statuses: 23 | try: 24 | status = Status.objects.get(name=name) 25 | except Status.DoesNotExist: 26 | print(f"nautobot_bgp_models: Unable to find status: {name} .. SKIPPING") 27 | continue 28 | 29 | if ct_model not in status.content_types.all(): 30 | status.content_types.add(ct_model) 31 | status.save() 32 | -------------------------------------------------------------------------------- /nautobot_bgp_models/template_content.py: -------------------------------------------------------------------------------- 1 | """Extensions of baseline Nautobot views.""" 2 | 3 | from nautobot.extras.plugins import PluginTemplateExtension 4 | 5 | from .models import AddressFamily, BGPRoutingInstance, PeerEndpoint 6 | 7 | 8 | class DevicePeerEndpoints(PluginTemplateExtension): # pylint: disable=abstract-method 9 | """Add PeerEndpoints to the right side of the Device page.""" 10 | 11 | model = "dcim.device" 12 | 13 | def right_page(self): 14 | """Add content to the right side of the Devices detail view.""" 15 | endpoints = PeerEndpoint.objects.filter( 16 | routing_instance__device=self.context["object"], 17 | ) 18 | return self.render( 19 | "nautobot_bgp_models/inc/device_peer_endpoints.html", 20 | extra_context={"endpoints": endpoints}, 21 | ) 22 | 23 | 24 | class DeviceAddressFamilies(PluginTemplateExtension): # pylint: disable=abstract-method 25 | """Add AddressFamilies to the right side of the Device page.""" 26 | 27 | model = "dcim.device" 28 | 29 | def right_page(self): 30 | """Add content to the right side of the Device detail view.""" 31 | address_families = AddressFamily.objects.filter( 32 | routing_instance__device=self.context["object"], 33 | ) 34 | return self.render( 35 | "nautobot_bgp_models/inc/device_address_families.html", 36 | extra_context={"address_families": address_families}, 37 | ) 38 | 39 | 40 | class DeviceBgpRoutingInstances(PluginTemplateExtension): # pylint: disable=abstract-method 41 | """Add BGPRoutingInstance to the right side of the Device page.""" 42 | 43 | model = "dcim.device" 44 | 45 | def right_page(self): 46 | """Add content to the right side of the Device detail view.""" 47 | bgp_routing_instances = BGPRoutingInstance.objects.filter( 48 | device=self.context["object"], 49 | ) 50 | return self.render( 51 | "nautobot_bgp_models/inc/device_bgp_routing_instances.html", 52 | extra_context={"bgp_routing_instances": bgp_routing_instances}, 53 | ) 54 | 55 | 56 | template_extensions = [ 57 | DeviceBgpRoutingInstances, 58 | DeviceAddressFamilies, 59 | DevicePeerEndpoints, 60 | ] 61 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/addressfamily_retrieve.html: -------------------------------------------------------------------------------- 1 | {% extends 'generic/object_detail.html' %} 2 | {% load helpers %} 3 | 4 | {% block content_left_page %} 5 |
6 |
7 | BGP Address-Family 8 |
9 | 10 | 11 | 12 | {% if object.routing_instance and object.routing_instance.device %} 13 | 14 | {% else %} 15 | 16 | {% endif %} 17 | 18 | 19 | 20 | 21 | 22 |
Device{{ object.routing_instance.device }}None
Routing Instance{{ object.routing_instance }}
23 |
24 |
25 |
26 | Attributes 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {% if object.vrf %} 36 | 37 | {% else %} 38 | 39 | {% endif %} 40 | 41 |
AFI-SAFI{{ object.afi_safi }}
VRF{{ object.vrf }}None
42 |
43 | {% endblock content_left_page %} 44 | 45 | {% block extra_nav_tabs %} 46 | 49 | {% endblock extra_nav_tabs %} 50 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/autonomoussystem_retrieve.html: -------------------------------------------------------------------------------- 1 | {% extends 'generic/object_detail.html' %} 2 | {% load helpers %} 3 | 4 | {% block content_left_page %} 5 |
6 |
7 | BGP Autonomous System 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 33 | 34 |
ASN{{ object.asn }}
ASN - ASDOT{{ object.asn_asdot }}
Description{{ object.description }}
Status 25 | {{ object.get_status_display }} 26 |
Provider 31 | {% include "nautobot_bgp_models/inc/native_property.html" with property=object.provider %} 32 |
35 |
36 | {% endblock content_left_page %} 37 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/autonomoussystemrange_retrieve.html: -------------------------------------------------------------------------------- 1 | {% extends 'generic/object_detail.html' %} 2 | {% load helpers %} 3 | 4 | {% block content_left_page %} 5 |
6 |
7 | BGP Autonomous System Range 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
Start ASN{{ object.asn_min }}
End ASN{{ object.asn_max }}
Tenant{{ object.tenant | hyperlinked_object }}
Description{{ object.description }}
27 |
28 | {% endblock content_left_page %} 29 | 30 | {% block content_right_page %} 31 | {% include 'utilities/obj_table.html' with table=asn_range_table table_template='panel_table.html' heading='ASNs' bulk_edit_url='plugins:nautobot_bgp_models:autonomoussystem_bulk_edit' bulk_delete_url='plugins:nautobot_bgp_models:autonomoussystem_bulk_delete' %} 32 | {% endblock content_right_page %} 33 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/bgproutinginstance_retrieve.html: -------------------------------------------------------------------------------- 1 | {% extends 'generic/object_detail.html' %} 2 | {% load helpers %} 3 | 4 | {% block content_left_page %} 5 |
6 |
7 | BGP Routing Instance 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 | 24 | 25 | 26 | 33 | 34 |
Device{{ object.device }}
Router ID 17 | {% if object.router_id %} 18 | {{ object.router_id }} 19 | {% else %} 20 | None 21 | {% endif %} 22 |
Autonomous System 27 | {% if object.autonomous_system %} 28 | {{ object.autonomous_system }} 29 | {% else %} 30 | None 31 | {% endif %} 32 |
35 |
36 | {% endblock content_left_page %} 37 | {% block content_right_page %} 38 |
39 |
40 | Address-Families 41 |
42 | 43 | {% for af in object.address_families.all %} 44 | 45 | 46 | 47 | {% endfor %} 48 |
{{ af.afi_safi }}
49 | 55 |
56 |
57 |
58 | Peer Groups 59 |
60 | 61 | {% for pg in object.peer_groups.all %} 62 | 63 | 64 | 65 | {% endfor %} 66 |
{{ pg.name }}
67 | 73 |
74 | {% endblock content_right_page %} 75 | 76 | {% block extra_nav_tabs %} 77 | 80 | {% endblock extra_nav_tabs %} 81 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/extra_attributes.html: -------------------------------------------------------------------------------- 1 | {% extends base_template %} 2 | {% load helpers %} 3 | 4 | {% block content %} 5 |
6 |
7 |
8 |
9 | Rendered BGP Extra Attributes 10 | {% include 'extras/inc/json_format.html' %} 11 |
12 |
13 | {% include 'extras/inc/json_data.html' with data=rendered_context format=format %} 14 |
15 |
16 |
17 |
18 |
19 |
20 | BGP object's extra attributes 21 |
22 |
23 | {% if object.extra_attributes %} 24 | {% include 'extras/inc/json_data.html' with data=object.extra_attributes format=format %} 25 | {% else %} 26 | None 27 | {% endif %} 28 |
29 | 35 |
36 |
37 |
38 | {% endblock content %} 39 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/inc/device_address_families.html: -------------------------------------------------------------------------------- 1 | {% load helpers %} 2 | {% if address_families %} 3 |
4 |
BGP Address-Families
5 | 6 | 7 | 8 | 9 | 10 | {% for af in address_families %} 11 | 12 | 15 | 18 | 19 | {% endfor %} 20 |
Routing InstanceAFI-SAFI
13 | {{ af.routing_instance }} 14 | 16 | {{ af }} 17 |
21 |
22 | {% endif %} 23 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/inc/device_bgp_routing_instances.html: -------------------------------------------------------------------------------- 1 | {% if bgp_routing_instances %} 2 |
3 |
BGP Routing Instances
4 | 5 | {% for bgp_routing_instance in bgp_routing_instances %} 6 | 7 | 10 | 11 | {% endfor %} 12 |
8 | {{ bgp_routing_instance }} 9 |
13 |
14 | {% else %} 15 |
16 |
BGP Routing Instances
17 | 18 | 19 | 22 | 23 |
20 | No routing instances 21 |
24 |
25 | {% endif %} 26 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/inc/device_peer_endpoints.html: -------------------------------------------------------------------------------- 1 | {% if endpoints %} 2 |
3 |
BGP Peerings
4 | 5 | {% for endpoint in endpoints %} 6 | 7 | 10 | 11 | 14 | 15 | {% endfor %} 16 |
8 | {{ endpoint }} 9 | ↔︎ 12 | {{ endpoint.peer }} 13 |
17 |
18 | {% else %} 19 |
20 |
BGP Peer Sessions
21 | 22 | 23 | 26 | 27 |
24 | No peerings 25 |
28 |
29 | {% endif %} 30 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/inc/inheritable_property.html: -------------------------------------------------------------------------------- 1 | {% load helpers %} 2 | {% if property.value is not None and property.value != "" %} 3 | {% if property.value.get_absolute_url %} 4 | {{ property.value }} 5 | {% elif property.value is True %} 6 | 7 | {% elif property.value is False %} 8 | 9 | {% else %} 10 | {{ property.value }} 11 | {% endif %} 12 | {% else %} 13 | {{ default | placeholder }} 14 | {% endif %} 15 | {% if property.inherited %} 16 | 17 | {{ property.source }} 18 | 19 | {% endif %} 20 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/inc/native_property.html: -------------------------------------------------------------------------------- 1 | {% load helpers %} 2 | {% get_attr property "color" as has_color %} 3 | {% if property is not None and property != "" %} 4 | {% if property.get_absolute_url %} 5 | {% if has_color %} 6 | 7 | 10 | 11 | {% else %} 12 | {{ property }} 13 | {% endif %} 14 | {% elif property is True %} 15 | 16 | {% elif property is False %} 17 | 18 | {% else %} 19 | {{ property }} 20 | {% endif %} 21 | {% else %} 22 | {{ default | placeholder }} 23 | {% endif %} 24 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/inc/peerendpoint.html: -------------------------------------------------------------------------------- 1 | {% load helpers %} 2 | 3 |
4 |
5 |
6 | {% if not endpoint and perms.nautobot_bgp_models.add_peerendpoint %} 7 | 8 | Add 9 | 10 | {% endif %} 11 | {% if endpoint %} 12 | 13 | Detail 14 | 15 | {% endif %} 16 | {% if endpoint and perms.nautobot_bgp_models.change_peerendpoint %} 17 | 18 | Edit 19 | 20 | {% endif %} 21 |
22 | Peer Endpoint - {{ side }} Side 23 |
24 | {% if endpoint %} 25 | 26 | 27 | {% if endpoint.routing_instance and endpoint.routing_instance.device %} 28 | 29 | 30 | {% elif not endpoint.routing_instance %} 31 | 32 | 35 | {% endif %} 36 | 37 | 38 | 39 | 46 | 47 | 48 | 49 | 52 | 53 | 54 | 55 | 56 | 57 |
Device{{ endpoint.routing_instance.device }}Provider 33 | {% include "nautobot_bgp_models/inc/native_property.html" with property=endpoint.autonomous_system.provider %} 34 |
Local IP Address 40 | {% if endpoint.fields_inherited.source_ip.value %} 41 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=endpoint.fields_inherited.source_ip %} 42 | {% elif endpoint.fields_inherited.source_interface.value %} 43 | {{ endpoint.local_ip }} via {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=endpoint.fields_inherited.source_interface %} 44 | {% endif %} 45 |
Autonomous System 50 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=endpoint.fields_inherited.autonomous_system %} 51 |
Peer Group{% include "nautobot_bgp_models/inc/native_property.html" with property=endpoint.peer_group %}
58 | {% else %} 59 |
60 | None 61 |
62 | {% endif %} 63 |
-------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/peerendpoint_retrieve.html: -------------------------------------------------------------------------------- 1 | {% extends 'generic/object_detail.html' %} 2 | {% load helpers %} 3 | 4 | {% block content_left_page %} 5 |
6 |
7 | BGP Peer Endpoint 8 |
9 | 10 | {% if object.routing_instance and object.routing_instance.device %} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% endif %} 20 | 21 | 22 | 29 | 30 | 31 | 32 | 39 |
Device{{ object.routing_instance.device }}
Routing Instance{{ object.routing_instance }}
Peer Group 23 | {% if object.peer_group %} 24 | {{ object.peer_group }} 25 | {% else %} 26 | None 27 | {% endif %} 28 |
Peering Session 33 | {% if object.peering %} 34 | {{ object.peering }} 35 | {% else %} 36 | None 37 | {% endif %} 38 |
40 |
41 |
42 |
43 | Authentication 44 |
45 | 46 | 47 | 48 | 51 | 52 |
Secrets 49 | {% include "nautobot_bgp_models/inc/native_property.html" with property=object.secret %} 50 |
53 |
54 |
55 |
56 | Attributes 57 |
58 | 59 | 60 | 61 | 64 | 65 | 66 | 67 | 70 | 71 | 72 | 73 | 76 | 77 | 78 | 79 | 82 | 83 | 84 | 85 | 88 | 89 |
Source IP Address 62 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=object.fields_inherited.source_ip %} 63 |
Source Interface 68 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=object.fields_inherited.source_interface %} 69 |
Description 74 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=object.fields_inherited.description %} 75 |
Enabled 80 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=object.fields_inherited.enabled %} 81 |
Autonomous System 86 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=object.fields_inherited.autonomous_system %} 87 |
90 |
91 | {% endblock content_left_page %} 92 | {% block content_right_page %} 93 |
94 |
95 | Peer Endpoint Address-Families 96 |
97 | 98 | {% for af in object.address_families.all %} 99 | 100 | 101 | 102 | {% endfor %} 103 |
{{ af.afi_safi }}
104 | 110 |
111 |
112 |
113 | Remote Peer Information 114 |
115 | 116 | 117 | 118 | 125 | 126 |
Remote Peer Endpoint 119 | {% if object.peer %} 120 | {{ object.peer }} 121 | {% else %} 122 | None 123 | {% endif %} 124 |
127 |
128 | {% endblock content_right_page %} 129 | 130 | {% block extra_nav_tabs %} 131 | 134 | {% endblock extra_nav_tabs %} 135 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/peerendpointaddressfamily_retrieve.html: -------------------------------------------------------------------------------- 1 | {% extends 'generic/object_detail.html' %} 2 | {% load helpers %} 3 | 4 | {% block content_left_page %} 5 |
6 |
7 | BGP Peer-Endpoint Address-Family 8 |
9 | 10 | 11 | 12 | {% if object.peer_endpoint.routing_instance and object.peer_endpoint.routing_instance.device %} 13 | 14 | {% else %} 15 | 16 | {% endif %} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 29 | 30 |
Device{{ object.peer_endpoint.routing_instance.device }}None
Routing Instance{{ object.peer_endpoint.routing_instance }}
Peer Endpoint 25 | 26 | {{ object.peer_endpoint }} 27 | 28 |
31 |
32 |
33 |
34 | Attributes 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 |
AFI-SAFI{{ object.afi_safi }}
Multipath 44 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=object.fields_inherited.multipath %} 45 |
48 |
49 | {% endblock content_left_page %} 50 | {% block content_right_page %} 51 |
52 |
Policy
53 | 54 | 55 | 56 | 59 | 60 | 61 | 62 | 65 | 66 |
Import Policy 57 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=object.fields_inherited.import_policy %} 58 |
Export Policy 63 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=object.fields_inherited.export_policy %} 64 |
67 |
68 | {% endblock content_right_page %} 69 | 70 | {% block extra_nav_tabs %} 71 | 74 | {% endblock extra_nav_tabs %} 75 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/peergroup_retrieve.html: -------------------------------------------------------------------------------- 1 | {% extends 'generic/object_detail.html' %} 2 | {% load helpers %} 3 | {% load static %} 4 | 5 | {% block content_left_page %} 6 |
7 |
8 | BGP Peer Group 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {% if object.routing_instance and object.routing_instance.device %} 18 | 19 | {% else %} 20 | 21 | {% endif %} 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {% if object.vrf %} 30 | 31 | {% else %} 32 | 33 | {% endif %} 34 | 35 |
Name{{ object.name }}
Device{{ object.routing_instance.device }}None
Routing Instance{{ object.routing_instance }}
VRF{{ object.vrf }}None
36 |
37 |
38 |
39 | Peer Group Template 40 |
41 | 42 | 43 | 44 | 47 | 48 |
Template 45 | {% include "nautobot_bgp_models/inc/native_property.html" with property=object.peergroup_template %} 46 |
49 |
50 |
51 |
52 | Authentication 53 |
54 | 55 | 56 | 57 | 60 | 61 |
Secrets 58 | {% include "nautobot_bgp_models/inc/native_property.html" with property=object.secret %} 59 |
62 |
63 |
64 |
65 | Attributes 66 |
67 | 68 | 69 | 70 | 73 | 74 | 75 | 76 | 79 | 80 | 81 | 82 | 85 | 86 | 87 | 88 | 91 | 92 | 93 | 94 | 97 | 98 |
Source IP Address 71 | {% include "nautobot_bgp_models/inc/native_property.html" with property=object.source_ip %} 72 |
Source Interface 77 | {% include "nautobot_bgp_models/inc/native_property.html" with property=object.source_interface %} 78 |
Description 83 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=object.fields_inherited.description %} 84 |
Enabled 89 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=object.fields_inherited.enabled %} 90 |
Autonomous System 95 | {% include "nautobot_bgp_models/inc/inheritable_property.html" with property=object.fields_inherited.autonomous_system %} 96 |
99 |
100 | {% endblock content_left_page %} 101 | {% block content_right_page %} 102 |
103 |
104 | Peer Group Address-Families 105 |
106 | 107 | {% for af in object.address_families.all %} 108 | 109 | 110 | 111 | {% endfor %} 112 |
{{ af.afi_safi }}
113 | 119 |
120 |
121 |
122 | Peerings In This Group 123 |
124 | 125 | {% for endpoint in object.endpoints.all %} 126 | 127 | 128 | 129 | 130 | 131 | {% endfor %} 132 |
{{ endpoint }}peered to{{ endpoint.peer }}
133 |
134 | {% endblock content_right_page %} 135 | 136 | {% block extra_nav_tabs %} 137 | 140 | {% endblock extra_nav_tabs %} 141 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/peergroupaddressfamily_retrieve.html: -------------------------------------------------------------------------------- 1 | {% extends 'generic/object_detail.html' %} 2 | {% load helpers %} 3 | 4 | {% block content_left_page %} 5 |
6 |
7 | BGP Peer-Group Address-Family 8 |
9 | 10 | 11 | 12 | {% if object.peer_group.routing_instance and object.peer_group.routing_instance.device %} 13 | 14 | {% else %} 15 | 16 | {% endif %} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 29 | 30 |
Device{{ object.peer_group.routing_instance.device }}None
Routing Instance{{ object.peer_group.routing_instance }}
Peer Group 25 | 26 | {{ object.peer_group }} 27 | 28 |
31 |
32 |
33 |
34 | Attributes 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
AFI-SAFI{{ object.afi_safi }}
Multipath{{ object.multipath | placeholder }}
46 |
47 | {% endblock content_left_page %} 48 | {% block content_right_page %} 49 |
50 |
Policy
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
Import Policy{{ object.import_policy | placeholder }}
Export Policy{{ object.export_policy | placeholder }}
61 |
62 | {% endblock content_right_page %} 63 | 64 | {% block extra_nav_tabs %} 65 | 68 | {% endblock extra_nav_tabs %} 69 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/peergrouptemplate_retrieve.html: -------------------------------------------------------------------------------- 1 | {% extends 'generic/object_detail.html' %} 2 | {% load helpers %} 3 | 4 | {% block content_left_page %} 5 |
6 |
7 | BGP Peer Group Template 8 |
9 | 10 | 11 | 12 | 13 | 14 |
Name{{ object.name }}
15 |
16 |
17 |
18 | Authentication 19 |
20 | 21 | 22 | 23 | 26 | 27 |
Secrets 24 | {% include "nautobot_bgp_models/inc/native_property.html" with property=object.secret %} 25 |
28 |
29 |
30 |
31 | Attributes 32 |
33 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | 45 | 46 | 47 | 48 | 51 | 52 | 53 | 54 | 57 | 58 |
Description 37 | {% include "nautobot_bgp_models/inc/native_property.html" with property=object.description %} 38 |
Role 43 | {% include "nautobot_bgp_models/inc/native_property.html" with property=object.role %} 44 |
Enabled 49 | {% include "nautobot_bgp_models/inc/native_property.html" with property=object.enabled %} 50 |
Autonomous System 55 | {% include "nautobot_bgp_models/inc/native_property.html" with property=object.autonomous_system %} 56 |
59 |
60 | {% endblock content_left_page %} 61 | 62 | {% block extra_nav_tabs %} 63 | 66 | {% endblock extra_nav_tabs %} 67 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/peering_add.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% load helpers %} 4 | {% load form_helpers %} 5 | {% block content %} 6 |
7 | {% csrf_token %} 8 | {% for form in forms %} 9 | {% for field in form.hidden_fields %} 10 | {{ field }} 11 | {% endfor %} 12 | {% if form.non_field_errors %} 13 |
14 |
15 |
16 |
Errors ({{ form.prefix | bettertitle }})
17 |
18 | {{ form.non_field_errors }} 19 |
20 |
21 |
22 |
23 | {% endif %} 24 | {% endfor %} 25 |

{% block title %}Create peering{% endblock %}

26 |
27 |
28 |
29 |
Endpoint A
30 |
31 | {% render_form peerendpoint_a_form %} 32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 |
40 |
Endpoint Z
41 |
42 | {% render_form peerendpoint_z_form %} 43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
Peering
51 |
52 | {% render_form peering_form %} 53 |
54 |
55 |
56 |
57 |
58 |
59 | 60 | Cancel 61 |
62 |
63 |
64 | {% endblock %} 65 | -------------------------------------------------------------------------------- /nautobot_bgp_models/templates/nautobot_bgp_models/peering_retrieve.html: -------------------------------------------------------------------------------- 1 | {% extends 'generic/object_detail.html' %} 2 | {% load helpers %} 3 | 4 | {% block content_left_page %} 5 |
6 |
7 | BGP Peering 8 |
9 | 10 | 11 | 12 | 17 | 18 |
Status 13 | 14 | {{ object.get_status_display }} 15 | 16 |
19 |
20 | {% endblock content_left_page %} 21 | {% block content_right_page %} 22 | {% include 'nautobot_bgp_models/inc/peerendpoint.html' with endpoint=object.endpoint_a side='A' %} 23 | {% include 'nautobot_bgp_models/inc/peerendpoint.html' with endpoint=object.endpoint_z side='Z' %} 24 | {% endblock content_right_page %} 25 | -------------------------------------------------------------------------------- /nautobot_bgp_models/tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Unit tests for nautobot_bgp_models app.""" 2 | -------------------------------------------------------------------------------- /nautobot_bgp_models/tests/test_basic.py: -------------------------------------------------------------------------------- 1 | """Basic tests that do not require Django.""" 2 | 3 | import os 4 | import unittest 5 | 6 | import toml 7 | 8 | 9 | class TestDocsPackaging(unittest.TestCase): 10 | """Test Version in doc requirements is the same pyproject.""" 11 | 12 | def test_version(self): 13 | """Verify that pyproject.toml dev dependencies have the same versions as in the docs requirements.txt.""" 14 | parent_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) 15 | poetry_path = os.path.join(parent_path, "pyproject.toml") 16 | poetry_details = toml.load(poetry_path)["tool"]["poetry"]["group"]["dev"]["dependencies"] 17 | with open(f"{parent_path}/docs/requirements.txt", "r", encoding="utf-8") as file: 18 | requirements = [line for line in file.read().splitlines() if (len(line) > 0 and not line.startswith("#"))] 19 | for pkg in requirements: 20 | package_name = pkg 21 | if len(pkg.split("==")) == 2: # noqa: PLR2004 22 | package_name, version = pkg.split("==") 23 | else: 24 | version = "*" 25 | self.assertEqual(poetry_details[package_name], version) 26 | -------------------------------------------------------------------------------- /nautobot_bgp_models/tests/test_helpers.py: -------------------------------------------------------------------------------- 1 | """Unit test automation for Helper methods in nautobot_bgp_models.""" 2 | 3 | from django.contrib.contenttypes.models import ContentType 4 | from django.test import TestCase 5 | from nautobot.extras.models import Status 6 | 7 | from nautobot_bgp_models import models 8 | from nautobot_bgp_models.helpers import add_available_asns 9 | 10 | 11 | class AddAvailableAsns(TestCase): 12 | """Test BGP helper methods.""" 13 | 14 | @classmethod 15 | def setUpTestData(cls): 16 | """One-time class data setup.""" 17 | status_active = Status.objects.get(name__iexact="active") 18 | status_active.content_types.add(ContentType.objects.get_for_model(models.AutonomousSystem)) 19 | 20 | cls.range_50_60 = models.AutonomousSystemRange.objects.create( 21 | name="Range 50 60", asn_min=50, asn_max=60, description="Test Description 1" 22 | ) 23 | 24 | cls.range_90_120 = models.AutonomousSystemRange.objects.create( 25 | name="Range 90 120", asn_min=90, asn_max=120, description="Test Description 2" 26 | ) 27 | 28 | cls.range_100_110 = models.AutonomousSystemRange.objects.create( 29 | name="Range 100 110", asn_min=100, asn_max=110, description="Test Description 3" 30 | ) 31 | cls.asn_100 = models.AutonomousSystem.objects.create(asn=100, status=status_active) 32 | cls.asn_110 = models.AutonomousSystem.objects.create(asn=110, status=status_active) 33 | 34 | def test_range_boundary(self): 35 | """Test asns availability - boundary.""" 36 | instance = self.range_100_110 37 | asns = models.AutonomousSystem.objects.filter(asn__gte=instance.asn_min, asn__lte=instance.asn_max) 38 | expected_availability = [ 39 | self.asn_100, 40 | {"asn": 101, "available": 9}, 41 | self.asn_110, 42 | ] 43 | 44 | self.assertEqual(expected_availability, add_available_asns(instance=instance, asns=asns)) 45 | 46 | def test_range_middle(self): 47 | """Test asns availability - middle.""" 48 | instance = self.range_90_120 49 | asns = models.AutonomousSystem.objects.filter(asn__gte=instance.asn_min, asn__lte=instance.asn_max) 50 | expected_availability = [ 51 | {"asn": 90, "available": 10}, 52 | self.asn_100, 53 | {"asn": 101, "available": 9}, 54 | self.asn_110, 55 | {"asn": 111, "available": 10}, 56 | ] 57 | 58 | self.assertEqual(expected_availability, add_available_asns(instance=instance, asns=asns)) 59 | 60 | def test_range_empty(self): 61 | """Test asns availability - empty.""" 62 | instance = self.range_50_60 63 | asns = models.AutonomousSystem.objects.filter(asn__gte=instance.asn_min, asn__lte=instance.asn_max) 64 | expected_availability = [ 65 | {"asn": 50, "available": 11}, 66 | ] 67 | 68 | self.assertEqual(expected_availability, add_available_asns(instance=instance, asns=asns)) 69 | -------------------------------------------------------------------------------- /nautobot_bgp_models/urls.py: -------------------------------------------------------------------------------- 1 | """Django urlpatterns declaration for nautobot_bgp_models app.""" 2 | 3 | from django.templatetags.static import static 4 | from django.urls import path 5 | from django.views.generic import RedirectView 6 | from nautobot.core.views.routers import NautobotUIViewSetRouter 7 | 8 | from . import models, views 9 | 10 | app_name = "nautobot_bgp_models" 11 | router = NautobotUIViewSetRouter() 12 | router.register("autonomous-systems", views.AutonomousSystemUIViewSet) 13 | router.register("autonomous-system-ranges", views.AutonomousSystemRangeUIViewSet) 14 | router.register("routing-instances", views.BGPRoutingInstanceUIViewSet) 15 | router.register("peer-groups", views.PeerGroupUIViewSet) 16 | router.register("peer-group-templates", views.PeerGroupTemplateUIViewSet) 17 | router.register("peer-endpoints", views.PeerEndpointUIViewSet) 18 | router.register("peerings", views.PeeringUIViewSet) 19 | router.register("address-families", views.AddressFamilyUIViewSet) 20 | router.register("peer-group-address-families", views.PeerGroupAddressFamilyUIViewSet) 21 | router.register("peer-endpoint-address-families", views.PeerEndpointAddressFamilyUIViewSet) 22 | 23 | urlpatterns = [ 24 | # Extra Attribute views. 25 | path( 26 | "routing-instances//extra-attributes/", 27 | views.BgpExtraAttributesView.as_view(), 28 | name="bgproutinginstance_extraattributes", 29 | kwargs={"model": models.BGPRoutingInstance}, 30 | ), 31 | path( 32 | "peer-groups//extra-attributes/", 33 | views.BgpExtraAttributesView.as_view(), 34 | name="peergroup_extraattributes", 35 | kwargs={"model": models.PeerGroup}, 36 | ), 37 | path( 38 | "peer-group-templates//extra-attributes/", 39 | views.BgpExtraAttributesView.as_view(), 40 | name="peergrouptemplate_extraattributes", 41 | kwargs={"model": models.PeerGroupTemplate}, 42 | ), 43 | path( 44 | "peer-endpoints//extra-attributes/", 45 | views.BgpExtraAttributesView.as_view(), 46 | name="peerendpoint_extraattributes", 47 | kwargs={"model": models.PeerEndpoint}, 48 | ), 49 | path( 50 | "address-families//extra-attributes/", 51 | views.BgpExtraAttributesView.as_view(), 52 | name="addressfamily_extraattributes", 53 | kwargs={"model": models.AddressFamily}, 54 | ), 55 | path( 56 | "peer-group-address-families//extra-attributes/", 57 | views.BgpExtraAttributesView.as_view(), 58 | name="peergroupaddressfamily_extraattributes", 59 | kwargs={"model": models.PeerGroupAddressFamily}, 60 | ), 61 | path( 62 | "peer-endpoint-address-families//extra-attributes/", 63 | views.BgpExtraAttributesView.as_view(), 64 | name="peerendpointaddressfamily_extraattributes", 65 | kwargs={"model": models.PeerEndpointAddressFamily}, 66 | ), 67 | path("peerings/add/", views.PeeringAddView.as_view(), name="peering_add"), 68 | path("docs/", RedirectView.as_view(url=static("nautobot_bgp_models/docs/index.html")), name="docs"), 69 | ] 70 | urlpatterns += router.urls 71 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "nautobot-bgp-models" 3 | version = "2.3.1a0" 4 | description = "Nautobot BGP Models App" 5 | authors = ["Network to Code, LLC "] 6 | license = "Apache-2.0" 7 | readme = "README.md" 8 | homepage = "https://github.com/nautobot/nautobot-app-bgp-models" 9 | repository = "https://github.com/nautobot/nautobot-app-bgp-models" 10 | documentation = "https://docs.nautobot.com/projects/bgp-models/en/latest/" 11 | keywords = ["nautobot", "nautobot-app", "nautobot-plugin"] 12 | classifiers = [ 13 | "Intended Audience :: Developers", 14 | "Development Status :: 5 - Production/Stable", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.8", 17 | "Programming Language :: Python :: 3.9", 18 | "Programming Language :: Python :: 3.10", 19 | "Programming Language :: Python :: 3.11", 20 | "Programming Language :: Python :: 3.12", 21 | ] 22 | packages = [ 23 | { include = "nautobot_bgp_models" }, 24 | ] 25 | include = [ 26 | # Poetry by default will exclude files that are in .gitignore 27 | { path = "nautobot_bgp_models/static/nautobot_bgp_models/docs/**/*", format = ["sdist", "wheel"] } 28 | ] 29 | 30 | [tool.poetry.dependencies] 31 | python = ">=3.8,<3.13" 32 | # Used for local development 33 | nautobot = "^2.0.3" 34 | netutils = "^1.6.0" 35 | toml = "^0.10.2" 36 | 37 | [tool.poetry.group.dev.dependencies] 38 | coverage = "*" 39 | django-debug-toolbar = "*" 40 | invoke = "*" 41 | ipython = "*" 42 | pylint = "*" 43 | pylint-django = "*" 44 | pylint-nautobot = "*" 45 | ruff = "0.5.5" 46 | yamllint = "*" 47 | toml = "*" 48 | # Python implementation of markdownlint 49 | pymarkdownlnt = [ 50 | {version = "0.9.26", python = "~3.8.0"}, 51 | {version = "~0.9.29", python = ">=3.9,<3.13"}, 52 | ] 53 | Markdown = "*" 54 | # Render custom markdown for version added/changed/remove notes 55 | markdown-version-annotations = "1.0.1" 56 | # Rendering docs to HTML 57 | mkdocs = "1.6.0" 58 | # Material for MkDocs theme 59 | mkdocs-material = "9.5.32" 60 | # Automatic documentation from sources, for MkDocs 61 | mkdocstrings = "0.25.2" 62 | mkdocstrings-python = "1.10.8" 63 | mkdocs-autorefs = "1.2.0" 64 | griffe = "1.1.1" 65 | towncrier = ">=23.6.0,<=24.8.0" 66 | to-json-schema = "*" 67 | jsonschema = "*" 68 | 69 | [tool.poetry.extras] 70 | all = [ 71 | ] 72 | 73 | [tool.pylint.master] 74 | # Include the pylint_django plugin to avoid spurious warnings about Django patterns 75 | load-plugins = "pylint_django, pylint_nautobot" 76 | ignore = ".venv" 77 | 78 | [tool.pylint.basic] 79 | # No docstrings required for private methods (Pylint default), or for test_ functions, or for inner Meta classes. 80 | no-docstring-rgx = "^(_|test_|Meta$)" 81 | 82 | [tool.pylint.messages_control] 83 | disable = [ 84 | "line-too-long", 85 | "nb-use-fields-all", 86 | "too-few-public-methods", 87 | "too-many-ancestors", 88 | "too-many-lines", 89 | ] 90 | 91 | [tool.pylint.miscellaneous] 92 | # Don't flag TODO as a failure, let us commit with things that still need to be done in the code 93 | notes = """, 94 | FIXME, 95 | XXX, 96 | """ 97 | 98 | [tool.pylint.similarities] 99 | min-similarity-lines = 15 100 | 101 | [tool.pylint-nautobot] 102 | supported_nautobot_versions = [ 103 | "2.0.3" 104 | ] 105 | 106 | [tool.ruff] 107 | line-length = 120 108 | target-version = "py38" 109 | 110 | [tool.ruff.lint] 111 | select = [ 112 | "D", # pydocstyle 113 | "F", "E", "W", # flake8 114 | "S", # bandit 115 | "I", # isort 116 | ] 117 | ignore = [ 118 | # Produces a lot of issues in the current codebase. 119 | "D106", # Missing docstring in public nested class 120 | 121 | # warning: `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible. 122 | "D203", # 1 blank line required before class docstring 123 | 124 | # D212 is enabled by default in google convention, and complains if we have a docstring like: 125 | # """ 126 | # My docstring is on the line after the opening quotes instead of on the same line as them. 127 | # """ 128 | # We've discussed and concluded that we consider this to be a valid style choice. 129 | "D212", # Multi-line docstring summary should start at the first line 130 | "D213", # Multi-line docstring summary should start at the second line 131 | 132 | # Produces a lot of issues in the current codebase. 133 | "D401", # First line of docstring should be in imperative mood 134 | "D407", # Missing dashed underline after section 135 | "D416", # Section name ends in colon 136 | "E501", # Line too long 137 | ] 138 | 139 | [tool.ruff.lint.pydocstyle] 140 | convention = "google" 141 | 142 | [tool.ruff.lint.per-file-ignores] 143 | "nautobot_bgp_models/migrations/*" = [ 144 | "D", 145 | ] 146 | "nautobot_bgp_models/tests/*" = [ 147 | "D", 148 | "S" 149 | ] 150 | 151 | [tool.coverage.run] 152 | disable_warnings = ["already-imported"] 153 | relative_files = true 154 | omit = [ 155 | # Skip Tests 156 | "*/tests/*", 157 | ] 158 | include = [ 159 | "nautobot_bgp_models/*", 160 | ] 161 | 162 | [tool.pymarkdown] 163 | # Seems to be not support for whitelisting rules: https://github.com/jackdewinter/pymarkdown/issues/1396 164 | plugins.md001.enabled = false 165 | plugins.md002.enabled = false 166 | plugins.md003.enabled = false 167 | plugins.md004.enabled = false 168 | plugins.md005.enabled = false 169 | plugins.md006.enabled = false 170 | plugins.md007.enabled = false 171 | plugins.md008.enabled = false 172 | plugins.md009.enabled = false 173 | plugins.md010.enabled = false 174 | plugins.md011.enabled = false 175 | plugins.md012.enabled = false 176 | plugins.md013.enabled = false 177 | plugins.md014.enabled = false 178 | plugins.md015.enabled = false 179 | plugins.md016.enabled = false 180 | plugins.md017.enabled = false 181 | plugins.md018.enabled = false 182 | plugins.md019.enabled = false 183 | plugins.md020.enabled = false 184 | plugins.md021.enabled = false 185 | plugins.md022.enabled = false 186 | plugins.md023.enabled = false 187 | plugins.md024.enabled = false 188 | plugins.md025.enabled = false 189 | plugins.md026.enabled = false 190 | plugins.md027.enabled = false 191 | plugins.md028.enabled = false 192 | plugins.md029.enabled = false 193 | plugins.md030.enabled = false 194 | plugins.md031.enabled = false 195 | plugins.md032.enabled = true # blanks-around-lists 196 | plugins.md033.enabled = false 197 | plugins.md034.enabled = false 198 | plugins.md035.enabled = false 199 | plugins.md036.enabled = false 200 | plugins.md037.enabled = false 201 | plugins.md038.enabled = false 202 | plugins.md039.enabled = false 203 | plugins.md040.enabled = false 204 | plugins.md041.enabled = false 205 | plugins.md042.enabled = false 206 | plugins.md043.enabled = false 207 | plugins.md044.enabled = false 208 | plugins.md045.enabled = false 209 | plugins.md046.enabled = false 210 | plugins.md047.enabled = false 211 | plugins.md048.enabled = false 212 | plugins.md049.enabled = false 213 | plugins.md050.enabled = false 214 | plugins.pml100.enabled = false 215 | plugins.pml101.enabled = true # list-anchored-indent 216 | plugins.pml102.enabled = false 217 | plugins.pml103.enabled = false 218 | 219 | [build-system] 220 | requires = ["poetry_core>=1.0.0"] 221 | build-backend = "poetry.core.masonry.api" 222 | 223 | [tool.towncrier] 224 | package = "nautobot_bgp_models" 225 | directory = "changes" 226 | filename = "docs/admin/release_notes/version_2.3.md" 227 | template = "development/towncrier_template.j2" 228 | start_string = "" 229 | issue_format = "[#{issue}](https://github.com/nautobot/nautobot-app-bgp-models/issues/{issue})" 230 | 231 | [[tool.towncrier.type]] 232 | directory = "security" 233 | name = "Security" 234 | showcontent = true 235 | 236 | [[tool.towncrier.type]] 237 | directory = "added" 238 | name = "Added" 239 | showcontent = true 240 | 241 | [[tool.towncrier.type]] 242 | directory = "changed" 243 | name = "Changed" 244 | showcontent = true 245 | 246 | [[tool.towncrier.type]] 247 | directory = "deprecated" 248 | name = "Deprecated" 249 | showcontent = true 250 | 251 | [[tool.towncrier.type]] 252 | directory = "removed" 253 | name = "Removed" 254 | showcontent = true 255 | 256 | [[tool.towncrier.type]] 257 | directory = "fixed" 258 | name = "Fixed" 259 | showcontent = true 260 | 261 | [[tool.towncrier.type]] 262 | directory = "dependencies" 263 | name = "Dependencies" 264 | showcontent = true 265 | 266 | [[tool.towncrier.type]] 267 | directory = "documentation" 268 | name = "Documentation" 269 | showcontent = true 270 | 271 | [[tool.towncrier.type]] 272 | directory = "housekeeping" 273 | name = "Housekeeping" 274 | showcontent = true 275 | --------------------------------------------------------------------------------