././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8233304 pynetbox-7.6.1/0000755000175100017510000000000015136437144013053 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.7933304 pynetbox-7.6.1/.github/0000755000175100017510000000000015136437144014413 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.7933304 pynetbox-7.6.1/.github/ISSUE_TEMPLATE/0000755000175100017510000000000015136437144016576 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.github/ISSUE_TEMPLATE/bug_report.yaml0000644000175100017510000000427715136437134021643 0ustar00runnerrunner--- name: 🐛 Bug Report description: Report a reproducible bug in the current release of pynetbox labels: ["app: pynetbox", "type: bug", "status: needs triage"] body: - type: markdown attributes: value: > **NOTE:** This form is only for reporting _reproducible bugs_ in a current pynetbox release. - type: input attributes: label: pynetbox version description: What version of pynetbox are you currently running? placeholder: v7.6.1 validations: required: true - type: input attributes: label: NetBox version description: What version of NetBox are you currently running? placeholder: v4.3.1 validations: required: true - type: dropdown attributes: label: Python version description: What version of Python are you currently running? options: - "3.10" - "3.11" - "3.12" validations: required: true - type: textarea attributes: label: Steps to Reproduce description: > Please provide a minimal working example to demonstrate the bug. Begin with the initialization of any necessary database objects and clearly enumerate each operation carried out. Ensure that your example is as concise as possible while adequately illustrating the issue. For example: ```python >>> import pynetbox >>> nb = pynebox.api('https://netbox.example.com', token='my-token') ``` Note: **do not utilize the demo instance** for replicating suspected bugs, as its data is subject to change or removal at any time. _Please refrain from including any confidential or sensitive information in your example._ validations: required: true - type: textarea attributes: label: Expected Behavior description: What did you expect to happen? placeholder: The script should execute without raising any errors or exceptions validations: required: true - type: textarea attributes: label: Observed Behavior description: What happened instead? placeholder: A TypeError exception was raised validations: required: true ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.github/ISSUE_TEMPLATE/config.yml0000644000175100017510000000137115136437134020567 0ustar00runnerrunner# Reference: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser blank_issues_enabled: false contact_links: - name: 📖 Contributing Policy url: https://github.com/netbox-community/netbox/blob/develop/CONTRIBUTING.md about: "Please read through our contributing policy before opening an issue or pull request." - name: ❓ Discussion url: https://github.com/netbox-community/pynetbox/discussions about: "If you're just looking for help, try starting a discussion instead." - name: 💬 Community Slack url: https://netdev.chat about: "Join #netbox on the NetDev Community Slack for assistance with installation issues and other problems." ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.github/ISSUE_TEMPLATE/deprecation.yaml0000644000175100017510000000127315136437134021761 0ustar00runnerrunner--- name: 🗑️ Deprecation description: The removal of an existing feature or resource labels: ["app: pynetbox", "type: deprecation"] body: - type: textarea attributes: label: Proposed Changes description: > Describe in detail the proposed changes. What is being removed? validations: required: true - type: textarea attributes: label: Justification description: Please provide justification for the proposed change(s). validations: required: true - type: textarea attributes: label: Impact description: List all areas of the application that will be affected by this change. validations: required: true ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.github/ISSUE_TEMPLATE/documentation_change.yaml0000644000175100017510000000164415136437134023644 0ustar00runnerrunner--- name: 📖 Documentation Change description: Suggest an addition or modification to the pynetbox documentation labels: ["app: pynetbox", "type: documentation", "status: needs triage"] body: - type: dropdown attributes: label: Change Type description: What type of change are you proposing? options: - Addition - Correction - Removal - Cleanup (formatting, typos, etc.) validations: required: true - type: dropdown attributes: label: Area description: To what section of the documentation does this change primarily pertain? options: - Endpoint - Response - Request - IPAM - Other validations: required: true - type: textarea attributes: label: Proposed Changes description: Describe the proposed changes and why they are necessary. validations: required: true ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.github/ISSUE_TEMPLATE/feature_request.yaml0000644000175100017510000000430715136437134022670 0ustar00runnerrunner--- name: ✨ Feature Request description: Propose a new pynetbox feature or enhancement labels: ["app: pynetbox", "type: feature", "status: needs triage"] body: - type: markdown attributes: value: > **NOTE:** This form is only for submitting well-formed proposals to extend or modify pynetbox in some way. If you're trying to solve a problem but can't figure out how, or if you still need time to work on the details of a proposed new feature, please start a [discussion](https://github.com/netbox-community/pynetbox/discussions) instead. - type: input attributes: label: pynetbox version description: What version of pynetbox are you currently running? placeholder: v7.1.0 validations: required: true - type: input attributes: label: NetBox version description: What version of NetBox are you currently running? placeholder: v3.6.0 validations: required: true - type: dropdown attributes: label: Feature type options: - Data model extension - New functionality - Change to existing functionality validations: required: true - type: textarea attributes: label: Proposed functionality description: > Describe in detail the new feature or behavior you are proposing. Include any specific changes to work flows, data models, and/or the user interface. The more detail you provide here, the greater chance your proposal has of being discussed. Feature requests which don't include an actionable implementation plan will be rejected. validations: required: true - type: textarea attributes: label: Use case description: > Explain how adding this functionality would benefit pynetbox users. What need does it address? validations: required: true - type: textarea attributes: label: External dependencies description: > List any new dependencies on external libraries or services that this new feature would introduce. For example, does the proposal require the installation of a new Python package? (Not all new features introduce new dependencies.) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.github/ISSUE_TEMPLATE/housekeeping.yaml0000644000175100017510000000151415136437134022150 0ustar00runnerrunner--- name: 🏡 Housekeeping description: A change pertaining to the codebase itself (developers only) labels: ["app: pynetbox", "type: housekeeping"] body: - type: markdown attributes: value: > **NOTE:** This template is for use by maintainers only. Please do not submit an issue using this template unless you have been specifically asked to do so. - type: textarea attributes: label: Proposed Changes description: > Describe in detail the new feature or behavior you'd like to propose. Include any specific changes to work flows, data models, or the user interface. validations: required: true - type: textarea attributes: label: Justification description: Please provide justification for the proposed change(s). validations: required: true ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.github/PULL_REQUEST_TEMPLATE.md0000644000175100017510000000113415136437134020212 0ustar00runnerrunner ### Fixes: #1234 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.7943304 pynetbox-7.6.1/.github/workflows/0000755000175100017510000000000015136437144016450 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.github/workflows/build-mkdocs.yml0000644000175100017510000000076715136437134021561 0ustar00runnerrunnername: Build MkDocs on: push: branches: - master - main permissions: contents: write jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: 3.x - run: pip install mkdocs-material mkdocs-autorefs mkdocs-material-extensions mkdocstrings mkdocstrings-python-legacy mkdocs-include-markdown-plugin pymdown-extensions markdown-include - run: mkdocs gh-deploy --force ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.github/workflows/publish.yml0000644000175100017510000000207415136437134020643 0ustar00runnerrunner# This workflow will upload a Python Package using Twine when a release is created # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries # This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support # documentation. name: Upload Python Package on: release: types: [published] permissions: contents: read jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v3 with: python-version: '3.x' - name: Install dependencies run: | python -m pip install --upgrade pip pip install build - name: Build package run: python -m build - name: Publish package uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.github/workflows/py3.yml0000644000175100017510000000142115136437134017703 0ustar00runnerrunnername: Py3 Test on: pull_request: {} push: branches: - master jobs: build: runs-on: ubuntu-latest strategy: matrix: python: ["3.10", "3.11", "3.12"] netbox: ["4.2", "4.3", "4.4"] steps: - uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - name: Install dev requirements run: pip install -r requirements-dev.txt . - name: Free up Docker resources run: | docker system prune -af --volumes docker network prune -f - name: Run Linter run: ruff check pynetbox/ tests/ - name: Run Tests run: pytest --netbox-versions=${{ matrix.netbox }} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.gitignore0000644000175100017510000000236315136437134015046 0ustar00runnerrunner# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib64/ lib/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ # Other git repos checked out locally .netbox-docker-*/ .devicetype-library/ # Visual Studio Code settings .vscode/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.pre-commit-config.yaml0000644000175100017510000000042415136437134017333 0ustar00runnerrunner# See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.1 hooks: - id: ruff name: "Ruff linter" args: [ pynetbox/, tests/ ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/.readthedocs.yaml0000644000175100017510000000042015136437134016275 0ustar00runnerrunnerversion: 2 build: os: ubuntu-22.04 tools: python: "3.11" # Build from the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # Explicitly set the version of Python and its requirements python: install: - requirements: docs/requirements.txt ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/CHANGELOG.md0000644000175100017510000000012715136437134014663 0ustar00runnerrunner For the list of changelog, please see the repository releases information in GitHub. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/LICENSE0000644000175100017510000002261415136437134014064 0ustar00runnerrunner Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2017 DigitalOcean././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8223305 pynetbox-7.6.1/PKG-INFO0000644000175100017510000001214615136437144014154 0ustar00runnerrunnerMetadata-Version: 2.4 Name: pynetbox Version: 7.6.1 Summary: NetBox API client library Home-page: https://github.com/netbox-community/pynetbox Author: Zach Moody, Arthur Hanson Author-email: ahanson@netboxlabs.com License: Apache2 Keywords: netbox Classifier: Intended Audience :: Developers Classifier: Development Status :: 5 - Production/Stable Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Description-Content-Type: text/markdown License-File: LICENSE Requires-Dist: requests<3.0,>=2.20.0 Requires-Dist: packaging Dynamic: author Dynamic: author-email Dynamic: classifier Dynamic: description Dynamic: description-content-type Dynamic: home-page Dynamic: keywords Dynamic: license Dynamic: license-file Dynamic: requires-dist Dynamic: summary # Pynetbox Python API client library for [NetBox](https://github.com/netbox-community/netbox). > **Note:** Version 6.7 and later of the library only supports NetBox 3.3 and above. ## Compatibility Each pyNetBox Version listed below has been tested with its corresponding NetBox Version. | NetBox Version | Plugin Version | |:--------------:|:--------------:| | 4.5 | 7.6.1 | | 4.5 | 7.6.0 | | 4.4 | 7.5.0 | | 4.3 | 7.5.0 | | 4.2 | 7.5.0 | | 4.1 | 7.5.0 | | 4.0.6 | 7.4.1 | | 4.0.0 | 7.3.4 | | 3.7 | 7.3.0 | | 3.6 | 7.2.0 | | 3.5 | 7.1.0 | | 3.3 | 7.0.0 | ## Installation To install run `pip install pynetbox`. Alternatively, you can clone the repo and run `python setup.py install`. ## Quick Start The full pynetbox API is documented on [GitHub Pages](https://netbox-community.github.io/pynetbox/), but the following should be enough to get started using it. To begin, import pynetbox and instantiate the API. ``` import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) ``` The first argument the .api() method takes is the NetBox URL. There are a handful of named arguments you can provide, but in most cases none are required to simply pull data. In order to write, the `token` argument should to be provided. ## Queries The pynetbox API is setup so that NetBox's apps are attributes of the `.api()` object, and in turn those apps have attribute representing each endpoint. Each endpoint has a handful of methods available to carry out actions on the endpoint. For example, in order to query all the objects in the `devices` endpoint you would do the following: ``` >>> devices = nb.dcim.devices.all() >>> for device in devices: ... print(device.name) ... test1-leaf1 test1-leaf2 test1-leaf3 >>> ``` Note that the all() and filter() methods are generators and return an object that can be iterated over only once. If you are going to be iterating over it repeatedly you need to either call the all() method again, or encapsulate the results in a `list` object like this: ``` >>> devices = list(nb.dcim.devices.all()) ``` ### Threading pynetbox supports multithreaded calls for `.filter()` and `.all()` queries. It is **highly recommended** you have `MAX_PAGE_SIZE` in your Netbox install set to anything *except* `0` or `None`. The default value of `1000` is usually a good value to use. To enable threading, add `threading=True` parameter to the `.api`: ```python nb = pynetbox.api( 'http://localhost:8000', threading=True, ) ``` ### Filters validation NetBox doesn't validate filters passed to the GET API endpoints, which are accessed with `.get()` and `.filter()`. If a filter is incorrect, NetBox silently returns the entire database table content. Pynetbox allows to check provided parameters against NetBox OpenAPI specification before doing the call, and raise an exception if a parameter is incorrect. This can be enabled globally by setting `strict_filters=True` in the API object initialization: ```python nb = pynetbox.api( 'http://localhost:8000', strict_filters=True, ) ``` This can also be enabled and disabled on a per-request basis: ```python # Disable for one request when enabled globally. # Will not raise an exception and return the entire Device table. nb.dcim.devices.filter(non_existing_filter="aaaa", strict_filters=False) # Enable for one request when not enabled globally. # Will raise an exception. nb.dcim.devices.filter(non_existing_filter="aaaa", strict_filters=True) ``` ## Running Tests First, create and activate a Python virtual environment in the pynetbox directory to isolate the project dependencies: ```python python3 -m venv venv source venv/bin/activate ``` Install both requirements files: ```python pip install -r requirements.txt pip install -r requirements-dev.txt ``` The test suite requires Docker to be installed and running, as it will download and launch netbox-docker containers during test execution. With Docker installed and running, execute the following command to run the test suite: ```python pytest ``` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/README.md0000644000175100017510000001033715136437134014335 0ustar00runnerrunner# Pynetbox Python API client library for [NetBox](https://github.com/netbox-community/netbox). > **Note:** Version 6.7 and later of the library only supports NetBox 3.3 and above. ## Compatibility Each pyNetBox Version listed below has been tested with its corresponding NetBox Version. | NetBox Version | Plugin Version | |:--------------:|:--------------:| | 4.5 | 7.6.1 | | 4.5 | 7.6.0 | | 4.4 | 7.5.0 | | 4.3 | 7.5.0 | | 4.2 | 7.5.0 | | 4.1 | 7.5.0 | | 4.0.6 | 7.4.1 | | 4.0.0 | 7.3.4 | | 3.7 | 7.3.0 | | 3.6 | 7.2.0 | | 3.5 | 7.1.0 | | 3.3 | 7.0.0 | ## Installation To install run `pip install pynetbox`. Alternatively, you can clone the repo and run `python setup.py install`. ## Quick Start The full pynetbox API is documented on [GitHub Pages](https://netbox-community.github.io/pynetbox/), but the following should be enough to get started using it. To begin, import pynetbox and instantiate the API. ``` import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) ``` The first argument the .api() method takes is the NetBox URL. There are a handful of named arguments you can provide, but in most cases none are required to simply pull data. In order to write, the `token` argument should to be provided. ## Queries The pynetbox API is setup so that NetBox's apps are attributes of the `.api()` object, and in turn those apps have attribute representing each endpoint. Each endpoint has a handful of methods available to carry out actions on the endpoint. For example, in order to query all the objects in the `devices` endpoint you would do the following: ``` >>> devices = nb.dcim.devices.all() >>> for device in devices: ... print(device.name) ... test1-leaf1 test1-leaf2 test1-leaf3 >>> ``` Note that the all() and filter() methods are generators and return an object that can be iterated over only once. If you are going to be iterating over it repeatedly you need to either call the all() method again, or encapsulate the results in a `list` object like this: ``` >>> devices = list(nb.dcim.devices.all()) ``` ### Threading pynetbox supports multithreaded calls for `.filter()` and `.all()` queries. It is **highly recommended** you have `MAX_PAGE_SIZE` in your Netbox install set to anything *except* `0` or `None`. The default value of `1000` is usually a good value to use. To enable threading, add `threading=True` parameter to the `.api`: ```python nb = pynetbox.api( 'http://localhost:8000', threading=True, ) ``` ### Filters validation NetBox doesn't validate filters passed to the GET API endpoints, which are accessed with `.get()` and `.filter()`. If a filter is incorrect, NetBox silently returns the entire database table content. Pynetbox allows to check provided parameters against NetBox OpenAPI specification before doing the call, and raise an exception if a parameter is incorrect. This can be enabled globally by setting `strict_filters=True` in the API object initialization: ```python nb = pynetbox.api( 'http://localhost:8000', strict_filters=True, ) ``` This can also be enabled and disabled on a per-request basis: ```python # Disable for one request when enabled globally. # Will not raise an exception and return the entire Device table. nb.dcim.devices.filter(non_existing_filter="aaaa", strict_filters=False) # Enable for one request when not enabled globally. # Will raise an exception. nb.dcim.devices.filter(non_existing_filter="aaaa", strict_filters=True) ``` ## Running Tests First, create and activate a Python virtual environment in the pynetbox directory to isolate the project dependencies: ```python python3 -m venv venv source venv/bin/activate ``` Install both requirements files: ```python pip install -r requirements.txt pip install -r requirements-dev.txt ``` The test suite requires Docker to be installed and running, as it will download and launch netbox-docker containers during test execution. With Docker installed and running, execute the following command to run the test suite: ```python pytest ``` ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.7963305 pynetbox-7.6.1/docs/0000755000175100017510000000000015136437144014003 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/IPAM.md0000644000175100017510000000764115136437134015062 0ustar00runnerrunner# IPAM This page documents special methods available for IPAM models in pyNetBox. !!! note "Standard API Operations" Standard CRUD operations (`.all()`, `.filter()`, `.get()`, `.create()`, `.update()`, `.delete()`) follow NetBox's REST API patterns. Refer to the [NetBox API documentation](https://demo.netbox.dev/api/docs/) for details on available endpoints and filters. ## Prefixes ### Available IPs The `available_ips` property provides access to view and create available IP addresses within a prefix. ::: pynetbox.models.ipam.Prefixes.available_ips handler: python options: show_source: true **Examples:** ```python prefix = nb.ipam.prefixes.get(prefix='10.0.0.0/24') # List available IP addresses available = prefix.available_ips.list() # [10.0.0.1/24, 10.0.0.2/24, 10.0.0.3/24, ...] # Create a single IP from available pool new_ip = prefix.available_ips.create() # Create multiple IPs new_ips = prefix.available_ips.create([{} for i in range(5)]) # Create IP with specific attributes new_ip = prefix.available_ips.create({ 'dns_name': 'server01.example.com', 'description': 'Web Server', 'status': 'active' }) ``` ### Available Prefixes The `available_prefixes` property provides access to view and create available child prefixes within a parent prefix. ::: pynetbox.models.ipam.Prefixes.available_prefixes handler: python options: show_source: true **Examples:** ```python prefix = nb.ipam.prefixes.get(prefix='10.0.0.0/16') # List available child prefixes available = prefix.available_prefixes.list() # [10.0.1.0/24, 10.0.2.0/23, 10.0.4.0/22, ...] # Create a child prefix new_prefix = prefix.available_prefixes.create({ 'prefix_length': 24, 'status': 'active', 'description': 'Server subnet' }) # Create multiple child prefixes new_prefixes = prefix.available_prefixes.create([ {'prefix_length': 24}, {'prefix_length': 24}, {'prefix_length': 25} ]) ``` ## IP Ranges ### Available IPs The `available_ips` property provides access to view and create available IP addresses within an IP range. ::: pynetbox.models.ipam.IpRanges.available_ips handler: python options: show_source: true **Examples:** ```python ip_range = nb.ipam.ip_ranges.get(1) # List available IPs in range available = ip_range.available_ips.list() # Create single IP from range new_ip = ip_range.available_ips.create() # Create multiple IPs new_ips = ip_range.available_ips.create([{} for i in range(10)]) # Create IP with attributes new_ip = ip_range.available_ips.create({ 'description': 'DHCP reservation', 'status': 'reserved' }) ``` ## VLAN Groups ### Available VLANs The `available_vlans` property provides access to view and create available VLANs within a VLAN group. ::: pynetbox.models.ipam.VlanGroups.available_vlans handler: python options: show_source: true **Examples:** ```python vlan_group = nb.ipam.vlan_groups.get(name='Production') # List available VLAN IDs available = vlan_group.available_vlans.list() # [10, 11, 12, 13, ...] # Create a VLAN from available IDs new_vlan = vlan_group.available_vlans.create({ 'name': 'NewVLAN', 'status': 'active' }) # NewVLAN (VID: 10) # Create VLAN with specific VID (must be in available range) new_vlan = vlan_group.available_vlans.create({ 'name': 'Servers', 'vid': 100, 'status': 'active' }) ``` ## ASN Ranges ### Available ASNs The `available_asns` property provides access to view and create available ASNs within an ASN range. ::: pynetbox.models.ipam.AsnRanges.available_asns handler: python options: show_source: true **Examples:** ```python asn_range = nb.ipam.asn_ranges.get(name='Private ASN Pool') # List available ASNs available = asn_range.available_asns.list() # [64512, 64513, 64514, ...] # Allocate a single ASN new_asn = asn_range.available_asns.create() # 64512 # Allocate multiple ASNs new_asns = asn_range.available_asns.create([{} for i in range(5)]) ``` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/advanced.md0000644000175100017510000001766715136437134016112 0ustar00runnerrunner# Advanced Usage ## Threading PyNetBox supports multithreaded calls for `.filter()` and `.all()` queries to significantly improve performance when fetching large datasets. !!! warning "NetBox Configuration Required" It is **highly recommended** you have `MAX_PAGE_SIZE` in your NetBox installation set to anything *except* `0` or `None`. The default value of `1000` is usually a good value to use. ### Enabling Threading Enable threading globally by passing `threading=True` to the API initialization: ```python import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='your-token', threading=True ) # Now all .all() and .filter() calls will use threading devices = nb.dcim.devices.all() # Fetches pages in parallel ``` ### How It Works When threading is enabled: - PyNetBox fetches multiple pages of results in parallel - Significantly faster for large result sets - Especially useful for `.all()` queries that span many pages - Works automatically with pagination ### Example ```python import pynetbox import time nb = pynetbox.api('http://localhost:8000', token='your-token') # Without threading start = time.time() devices = list(nb.dcim.devices.all()) print(f"Without threading: {time.time() - start:.2f}s") # With threading nb_threaded = pynetbox.api( 'http://localhost:8000', token='your-token', threading=True ) start = time.time() devices = list(nb_threaded.dcim.devices.all()) print(f"With threading: {time.time() - start:.2f}s") ``` ## Filter Validation NetBox doesn't validate filters passed to GET API endpoints (`.get()` and `.filter()`). If a filter is incorrect, NetBox silently returns the entire database table content, which can be slow and unexpected. PyNetBox can validate filter parameters against NetBox's OpenAPI specification before making the request, raising an exception if a parameter is invalid. ### Enabling Strict Filters Globally ```python import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='your-token', strict_filters=True # Enable validation globally ) # This will raise ParameterValidationError try: devices = nb.dcim.devices.filter(non_existing_filter='value') except pynetbox.core.query.ParameterValidationError as e: print(f"Invalid filter: {e}") ``` ### Per-Request Validation You can also enable or disable validation on a per-request basis: ```python nb = pynetbox.api('http://localhost:8000', token='your-token') # Enable for one request (when not globally enabled) try: devices = nb.dcim.devices.filter( non_existing_filter='aaaa', strict_filters=True ) except pynetbox.core.query.ParameterValidationError as e: print(f"Invalid filter: {e}") # Disable for one request (when globally enabled) nb_strict = pynetbox.api( 'http://localhost:8000', token='your-token', strict_filters=True ) # This won't raise an exception, but returns entire table devices = nb_strict.dcim.devices.filter( non_existing_filter='aaaa', strict_filters=False ) ``` ### Benefits of Strict Filters - **Catch typos early**: Find misspelled filter names before making requests - **Prevent full table scans**: Avoid accidentally fetching entire tables - **Better error messages**: Get clear feedback about invalid parameters - **Development aid**: Helpful during development to ensure correct filter usage ### Example ```python import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='your-token', strict_filters=True ) # Valid filter - works fine devices = nb.dcim.devices.filter(site='datacenter1') # Invalid filter - raises exception try: devices = nb.dcim.devices.filter(iste='datacenter1') # Typo: 'iste' instead of 'site' except pynetbox.core.query.ParameterValidationError as e: print(f"Error: {e}") # Error: 'iste' is not a valid filter parameter for dcim.devices ``` ## Custom Sessions Custom sessions can be used to modify the default HTTP behavior. Below are a few examples, most of them from [here](https://hodovi.ch/blog/advanced-usage-python-requests-timeouts-retries-hooks/). ### Headers To set a custom header on all requests. These headers are automatically merged with headers pynetbox sets itself. Example: ```python import pynetbox import requests session = requests.Session() session.headers = {"mycustomheader": "test"} nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) nb.http_session = session ``` ### SSL Verification To disable SSL verification. See [the docs](https://requests.readthedocs.io/en/stable/user/advanced/#ssl-cert-verification). Example: ```python import pynetbox import requests session = requests.Session() session.verify = False nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) nb.http_session = session ``` ### Timeouts Setting timeouts requires the use of Adapters. Example: ```python from requests.adapters import HTTPAdapter class TimeoutHTTPAdapter(HTTPAdapter): def __init__(self, *args, **kwargs): self.timeout = kwargs.get("timeout", 5) super().__init__(*args, **kwargs) def send(self, request, **kwargs): kwargs['timeout'] = self.timeout return super().send(request, **kwargs) adapter = TimeoutHTTPAdapter() session = requests.Session() session.mount("http://", adapter) session.mount("https://", adapter) nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) nb.http_session = session ``` ## File Uploads (Image Attachments) Pynetbox supports file uploads for endpoints that accept them, such as image attachments. When you pass a file-like object (anything with a `.read()` method) to `create()`, pynetbox automatically detects it and uses multipart/form-data encoding instead of JSON. ### Creating an Image Attachment ```python import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) # Attach an image to a device with open('/path/to/image.png', 'rb') as f: attachment = nb.extras.image_attachments.create( object_type='dcim.device', object_id=1, image=f, name='rack-photo.png' ) ``` ### Using io.BytesIO You can also use in-memory file objects: ```python import io import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) # Create image from bytes image_data = b'...' # Your image bytes file_obj = io.BytesIO(image_data) file_obj.name = 'generated-image.png' # Optional: set filename attachment = nb.extras.image_attachments.create( object_type='dcim.device', object_id=1, image=file_obj ) ``` ### Custom Filename and Content-Type For more control, pass a tuple instead of a file object: ```python with open('/path/to/image.png', 'rb') as f: attachment = nb.extras.image_attachments.create( object_type='dcim.device', object_id=1, image=('custom-name.png', f, 'image/png') ) ``` The tuple format is `(filename, file_object)` or `(filename, file_object, content_type)`. ## Multi-Format Responses Some endpoints support multiple response formats. The rack elevation endpoint can return both JSON data and SVG diagrams. ### Getting Rack Elevation as JSON By default, the elevation endpoint returns JSON data as a list of rack unit objects: ```python import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) rack = nb.dcim.racks.get(123) # Returns list of RU objects (default JSON response) units = rack.elevation.list() for unit in units: print(unit.id, unit.name) ``` ### Getting Rack Elevation as SVG Use the `render='svg'` parameter to get a graphical SVG diagram: ```python rack = nb.dcim.racks.get(123) # Returns raw SVG string svg_diagram = rack.elevation.list(render='svg') print(svg_diagram) # '...' # Save to file with open('rack-elevation.svg', 'w') as f: f.write(svg_diagram) ``` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/api.md0000644000175100017510000000413115136437134015074 0ustar00runnerrunner# API Core Classes This page documents the core classes that form pyNetBox's API structure. ## Overview PyNetBox uses a layered architecture to interact with NetBox: 1. **Api** - The main entry point that creates connections to NetBox 2. **App** - Represents NetBox applications (dcim, ipam, circuits, etc.) 3. **Endpoint** - Provides CRUD operations for specific API endpoints ```python import pynetbox # Create API connection (Api class) nb = pynetbox.api('http://localhost:8000', token='your-token') # Access an app (App class) nb.dcim # Returns an App instance # Access an endpoint (Endpoint class) nb.dcim.devices # Returns an Endpoint instance # Use endpoint methods devices = nb.dcim.devices.all() ``` ## Api Class The `Api` class is the main entry point for interacting with NetBox. It manages the HTTP session, authentication, and provides access to NetBox applications. ::: pynetbox.core.api.Api handler: python options: members: - __init__ - create_token - openapi - status - version - activate_branch show_source: true show_root_heading: true heading_level: 3 ## App Class The `App` class represents a NetBox application (such as dcim, ipam, circuits). When you access an attribute on the `Api` object, it returns an `App` instance. Accessing attributes on an `App` returns `Endpoint` objects. ::: pynetbox.core.app.App handler: python options: members: - config show_source: true show_root_heading: true heading_level: 3 ## Relationship to Endpoints When you access an attribute on an `App` object, it returns an [Endpoint](endpoint.md) instance: ```python # nb.dcim is an App instance # nb.dcim.devices is an Endpoint instance devices_endpoint = nb.dcim.devices # Endpoint provides CRUD methods all_devices = devices_endpoint.all() device = devices_endpoint.get(1) new_device = devices_endpoint.create(name='test', site=1, device_type=1, device_role=1) ``` See the [Endpoint documentation](endpoint.md) for details on available methods. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/branching.md0000644000175100017510000000533015136437134016260 0ustar00runnerrunner# Branching Plugin The NetBox branching plugin allows you to create and work with branches in NetBox, similar to version control systems. This enables you to make changes in isolation and merge them back to the main branch when ready. ## Activating Branches The `activate_branch` context manager allows you to perform operations within a specific branch's schema. All operations performed within the context manager will use that branch's schema. ```python import pynetbox # Initialize the API nb = pynetbox.api( "http://localhost:8000", token="your-token-here" ) # Get an existing branch branch = nb.plugins.branching.branches.get(id=1) # Activate the branch for operations with nb.activate_branch(branch): # All operations within this block will use the branch's schema sites = nb.dcim.sites.all() # Make changes to objects... # These changes will only exist in this branch ``` ## Waiting for Branch Status When working with branches, you often need to wait for certain status changes, such as when a branch becomes ready after creation or when a merge operation completes. The [tenacity](https://github.com/jd/tenacity) library provides a robust way to handle these waiting scenarios. First, install tenacity: ```bash pip install tenacity ``` Here's how to create a reusable function to wait for branch status changes: ```python from tenacity import retry, retry_if_result, stop_after_attempt, wait_exponential import pynetbox @retry( stop=stop_after_attempt(30), # Try for up to 30 attempts wait=wait_exponential( multiplier=1, min=4, max=60 ), # Wait between 4-60 seconds, increasing exponentially retry=retry_if_result(lambda x: not x), # Retry if the status check returns False ) def wait_for_branch_status(branch, target_status): """Wait for branch to reach a specific status, with exponential backoff.""" branch = nb.plugins.branching.branches.get(branch.id) return str(branch.status) == target_status # Example usage: branch = nb.plugins.branching.branches.create(name="my-branch") # Wait for branch to be ready wait_for_branch_status(branch, "Ready") # Get the latest branch status branch = nb.plugins.branching.branches.get(branch.id) print(f"Branch is now ready! Status: {branch.status}") ``` The function will: 1. Check the current status of the branch 2. If the status doesn't match the target status, it will retry with exponential backoff 3. Continue retrying until either: - The branch reaches the target status - The maximum number of attempts (30) is reached - The maximum wait time (60 seconds) is exceeded The exponential backoff ensures that we don't overwhelm the server with requests while still checking frequently enough to catch status changes quickly. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/circuits.md0000644000175100017510000000741415136437134016157 0ustar00runnerrunner# Circuits This page documents special methods available for Circuits models in pyNetBox. !!! note "Standard API Operations" Standard CRUD operations (`.all()`, `.filter()`, `.get()`, `.create()`, `.update()`, `.delete()`) follow NetBox's REST API patterns. Refer to the [NetBox API documentation](https://demo.netbox.dev/api/docs/) for details on available endpoints and filters. ## Circuit Terminations ### Cable Path Tracing Circuit terminations support cable path tracing through the `paths()` method. This method returns all cable paths that traverse through the circuit termination, showing the complete connectivity from origin to destination. **Example:** ```python # Get a circuit termination circuit_term = nb.circuits.circuit_terminations.get(circuit_id=123, term_side='A') # Get all cable paths through this termination paths = circuit_term.paths() # Each path contains origin, destination, and path segments for path_info in paths: print(f"Origin: {path_info['origin']}") print(f"Destination: {path_info['destination']}") print("Path segments:") for segment in path_info['path']: for obj in segment: print(f" - {obj}") # Example: Find what a circuit connects to circuit = nb.circuits.circuits.get(cid='CIRCUIT-001') terminations = nb.circuits.circuit_terminations.filter(circuit_id=circuit.id) for term in terminations: print(f"\nTermination {term.term_side}:") paths = term.paths() if paths: for path in paths: if path['destination']: print(f" Connected to: {path['destination']}") else: print(" No destination (incomplete path)") else: print(" No cable paths") ``` **Path Structure:** The `paths()` method returns a list of dictionaries, where each dictionary represents a complete cable path: - `origin`: The starting endpoint of the path (Record object or None if unconnected) - `destination`: The ending endpoint of the path (Record object or None if unconnected) - `path`: A list of path segments, where each segment is a list of Record objects representing the components in that segment (cables, terminations, interfaces, etc.) ## Virtual Circuits ### Overview Virtual circuits also support cable path tracing through the `paths()` method. **Example:** ```python # Get a virtual circuit vcircuit = nb.circuits.virtual_circuits.get(cid='VPLS-001') print(f"Virtual Circuit: {vcircuit.cid}") print(f"Provider Network: {vcircuit.provider_network.name}") print(f"Type: {vcircuit.type.name}") # List all terminations for a virtual circuit terminations = nb.circuits.virtual_circuit_terminations.filter( virtual_circuit_id=vcircuit.id ) for term in terminations: print(f"Termination Role: {term.role}") ``` ### Virtual Circuit Termination Path Tracing Virtual circuit terminations also support cable path tracing through the `paths()` method. **Example:** ```python # Get a virtual circuit termination vterm = nb.circuits.virtual_circuit_terminations.get( virtual_circuit_id=123, role='hub' ) # Get all cable paths through this termination paths = vterm.paths() # Analyze the connectivity for path_info in paths: print(f"Origin: {path_info['origin']}") print(f"Destination: {path_info['destination']}") print("Path segments:") for segment in path_info['path']: for obj in segment: print(f" - {obj}") # Example: Find all devices connected via a virtual circuit vcircuit = nb.circuits.virtual_circuits.get(cid='VPLS-001') terminations = nb.circuits.virtual_circuit_terminations.filter( virtual_circuit_id=vcircuit.id ) print(f"Virtual Circuit {vcircuit.cid} connectivity:") for term in terminations: paths = term.paths() if paths and paths[0]['destination']: print(f" {term.role}: {paths[0]['destination']}") ``` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/dcim.md0000644000175100017510000001117615136437134015246 0ustar00runnerrunner# DCIM This page documents special methods available for DCIM models in pyNetBox. !!! note "Standard API Operations" Standard CRUD operations (`.all()`, `.filter()`, `.get()`, `.create()`, `.update()`, `.delete()`) follow NetBox's REST API patterns. Refer to the [NetBox API documentation](https://demo.netbox.dev/api/docs/) for details on available endpoints and filters. ## Devices ### NAPALM Integration The `napalm` property provides access to NAPALM device data. ::: pynetbox.models.dcim.Devices.napalm handler: python options: show_source: true **Example:** ```python device = nb.dcim.devices.get(name='router1') # Get device facts facts = device.napalm.list(method='get_facts') print(facts) # Get interfaces interfaces = device.napalm.list(method='get_interfaces') # Get ARP table arp = device.napalm.list(method='get_arp_table') ``` ### Config Rendering The `render_config` property renders device configuration based on config contexts and templates. ::: pynetbox.models.dcim.Devices.render_config handler: python options: show_source: true **Example:** ```python device = nb.dcim.devices.get(name='switch1') config = device.render_config.create() print(config) ``` ## Racks ### Rack Units The `units` property provides access to rack unit information. ::: pynetbox.models.dcim.Racks.units handler: python options: show_source: true **Example:** ```python rack = nb.dcim.racks.get(name='RACK-01') units = rack.units.list() for unit in units: if unit.device: print(f"U{unit.name}: {unit.device.name}") else: print(f"U{unit.name}: Empty") ``` ### Rack Elevation The `elevation` property supports both JSON and SVG output for rack elevation diagrams. ::: pynetbox.models.dcim.Racks.elevation handler: python options: show_source: true **Examples:** ```python rack = nb.dcim.racks.get(name='RACK-01') # Get elevation as JSON (returns list of RU objects) elevation_data = rack.elevation.list() # Get elevation as SVG diagram svg_diagram = rack.elevation.list(render='svg') # Save SVG to file with open('rack-elevation.svg', 'w') as f: f.write(svg_diagram) ``` ## Cable Tracing Several DCIM models support cable path tracing through the `trace()` method. **Models with cable tracing:** - Interfaces - ConsolePorts - ConsoleServerPorts - PowerPorts - PowerOutlets - PowerFeeds **Example:** ```python # Trace a network interface interface = nb.dcim.interfaces.get(name='eth0', device='switch1') trace_result = interface.trace() # The trace returns a list of [terminations, cable, terminations] for item in trace_result: if isinstance(item, list): # Terminations for term in item: print(f" Termination: {term}") else: # Cable or None if item: print(f" Cable: {item.id} - {item.label}") else: print(" No cable") # Trace console port console = nb.dcim.console_ports.get(name='Console', device='router1') console_trace = console.trace() # Trace power connections power_port = nb.dcim.power_ports.get(name='PSU1', device='server1') power_trace = power_port.trace() ``` ## Cable Path Tracing (Pass-Through Ports) Front ports and rear ports use the `paths()` method instead of `trace()`. **Models with cable path tracing:** - FrontPorts - RearPorts **Example:** ```python # Get paths through a front port front_port = nb.dcim.front_ports.get(name='FrontPort1', device='patch-panel-1') paths = front_port.paths() # Each path contains origin, destination, and path segments for path_info in paths: print(f"Origin: {path_info['origin']}") print(f"Destination: {path_info['destination']}") print("Path segments:") for segment in path_info['path']: for obj in segment: print(f" - {obj}") # Get paths through a rear port rear_port = nb.dcim.rear_ports.get(name='RearPort1', device='patch-panel-1') rear_paths = rear_port.paths() # Access the complete path from origin to destination if rear_paths: first_path = rear_paths[0] if first_path['origin']: print(f"Cable path starts at: {first_path['origin']}") if first_path['destination']: print(f"Cable path ends at: {first_path['destination']}") ``` **Path Structure:** The `paths()` method returns a list of dictionaries, where each dictionary represents a complete cable path: - `origin`: The starting endpoint of the path (Record object or None if unconnected) - `destination`: The ending endpoint of the path (Record object or None if unconnected) - `path`: A list of path segments, where each segment is a list of Record objects representing the components in that segment (cables, terminations, etc.) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.7973304 pynetbox-7.6.1/docs/development/0000755000175100017510000000000015136437144016325 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/development/getting-started.md0000644000175100017510000000520615136437134021756 0ustar00runnerrunner# Getting Started This guide will help you get started with development on pynetbox. It covers setting up your development environment and running tests. ## Development Environment 1. Fork the pynetbox repository on GitHub 2. Clone your fork locally 3. Create a virtual environment and install development dependencies: ```bash python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate pip install -e ".[dev]" ``` ## Running Tests pynetbox uses pytest for testing. The test suite includes both unit tests and integration tests. ### Unit Tests To run the unit tests: ```bash pytest tests/unit ``` ### Integration Tests The integration tests require a running NetBox instance. The test suite uses pytest-docker to spin up NetBox instances in Docker containers. To run the integration tests: ```bash pytest tests/integration ``` You can specify which versions of NetBox to test against using the `--netbox-versions` flag: ```bash pytest tests/integration --netbox-versions 4.2 4.3 4.4 ``` ### Running Specific Tests You can run specific test files or test functions: ```bash # Run a specific test file pytest tests/unit/test_api.py # Run a specific test function pytest tests/unit/test_api.py::test_api_status # Run tests matching a pattern pytest -k "test_api" ``` ### Test Coverage To run tests with coverage reporting: ```bash pytest --cov=pynetbox tests/ ``` ## Submitting Pull Requests Once you're happy with your work and have verified that all tests pass, commit your changes and push it upstream to your fork. Always provide descriptive (but not excessively verbose) commit messages. Be sure to prefix your commit message with the word "Fixes" or "Closes" and the relevant issue number (with a hash mark). This tells GitHub to automatically close the referenced issue once the commit has been merged. ```bash git commit -m "Closes #1234: Add IPv5 support" git push origin ``` Once your fork has the new commit, submit a pull request to the pynetbox repo to propose the changes. Be sure to provide a detailed accounting of the changes being made and the reasons for doing so. Once submitted, a maintainer will review your pull request and either merge it or request changes. If changes are needed, you can make them via new commits to your fork: The pull request will update automatically. !!! warning Remember, pull requests are permitted only for **accepted** issues. If an issue you want to work on hasn't been approved by a maintainer yet, it's best to avoid risking your time and effort on a change that might not be accepted. (The one exception to this is trivial changes to the documentation or other non-critical resources.) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/development/index.md0000644000175100017510000000620115136437134017754 0ustar00runnerrunner# Development Thanks for your interest in contributing to pynetbox! This introduction covers a few important things to know before you get started. ## The Code pynetbox is maintained on [GitHub](https://github.com/netbox-community/pynetbox). GitHub also serves as one of our primary discussion forums. While all the code and discussion is publicly accessible, you'll need to register for a free GitHub account to engage in participation. Most people begin by forking the pynetbox repository under their own GitHub account to begin working on the code. There are two permanent branches in the repository: * `master` - Active development for the upcoming patch release. Pull requests will typically be based on this branch unless they introduce breaking changes that must be deferred until the next major release. * `feature` - New feature work to be introduced in the next major release. pynetbox components are arranged into modules: * `core/` - Core functionality including API interaction, response handling, and query building * `models/` - Model definitions for different NetBox object types * `tests/` - Test suite including unit and integration tests * `docs/` - Documentation files ## Proposing Changes All substantial changes made to the code base are tracked using GitHub issues. Feature requests, bug reports, and similar proposals must all be filed as issues and approved by a maintainer before work begins. This ensures that all changes to the code base are properly documented for future reference. To submit a new feature request or bug report for pynetbox, select and complete the appropriate issue template. Once your issue has been approved, you're welcome to submit a pull request containing your proposed changes. !!! note Avoid starting work on a proposal before it has been accepted. Not all proposed changes will be accepted, and we'd hate for you to waste time working on code that might not make it into the project. ## Getting Help There are two primary forums for getting assistance with pynetbox development: * [GitHub discussions](https://github.com/netbox-community/pynetbox/discussions) - The preferred forum for general discussion and support issues. Ideal for shaping a feature requests prior to submitting an issue. * [#netbox on NetDev Community Slack](https://netdev.chat) - Good for quick chats. Avoid any discussion that might need to be referenced later on, as the chat history is not retained indefinitely. !!! note Don't use GitHub issues to ask for help: These are reserved for proposed code changes only. ## Governance pynetbox follows the benevolent dictator model of governance, with the lead maintainer ultimately responsible for all changes to the code base. While community contributions are welcomed and encouraged, the lead maintainer's primary role is to ensure the project's long-term maintainability and continued focus on its primary functions. ## Licensing The entire pynetbox project is licensed as open source under the Apache 2.0 license. This is a very permissive license which allows unlimited redistribution of all code within the project. Note that all submissions to the project are subject to the same license. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/development/release-checklist.md0000644000175100017510000000316215136437134022237 0ustar00runnerrunner# Release Checklist This document outlines the steps required to prepare and publish a new release of pynetbox. ## Pre-Release Tasks 1. Ensure all tests are passing: ```bash pytest ``` 2. Update version number in `pynetbox/__init__.py` 3. Update documentation for any new features or changes 4. Check NetBox Docker releases: - Visit https://github.com/netbox-community/netbox-docker/releases - Review the latest NetBox Docker releases and their corresponding NetBox versions - Update supported NetBox versions in `tests/integration/conftest.py` if needed - Ensure the `get_netbox_docker_version_tag` function in `tests/integration/conftest.py` is updated with any new version mappings ## Release Tasks 1. Create a new release branch from `master`: ```bash git checkout master git pull git checkout -b release/vX.Y.Z ``` 2. Commit version and changelog updates: ```bash git commit -m "Prepare release vX.Y.Z" ``` 3. Create a pull request to merge the release branch into `master` 4. Once merged, use github to create a new release: 1. Go to the GitHub repository 2. Click "Releases" in the right sidebar 3. Click "Create a new release" 4. Create a new tag (e.g., vX.Y.Z) 5. Use the changelog content as the release description 6. Publish the release The GitHub release will automatically trigger the workflow to publish to PyPI. ## Supported NetBox Versions pynetbox aims to support the current and previous two minor versions of NetBox. The supported versions are defined in `tests/integration/conftest.py` and should be updated as part of the release process. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/endpoint.md0000644000175100017510000000221715136437134016146 0ustar00runnerrunner# Endpoint `Endpoint` objects provide CRUD operations for NetBox API endpoints. They are automatically created when you access attributes on [App](api.md#app-class) objects. ## Overview ```python import pynetbox nb = pynetbox.api('http://localhost:8000', token='your-token') # Accessing an attribute on an App returns an Endpoint devices = nb.dcim.devices # This is an Endpoint instance # Use Endpoint methods for CRUD operations all_devices = devices.all() device = devices.get(1) filtered = devices.filter(site='headquarters') new_device = devices.create(name='test', site=1, device_type=1, device_role=1) ``` ::: pynetbox.core.endpoint.Endpoint handler: python options: members: - all - choices - count - create - delete - filter - get - update show_source: true show_root_heading: true heading_level: 2 ::: pynetbox.core.endpoint.DetailEndpoint handler: python options: members: - create - list show_source: true show_root_heading: true heading_level: 2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/getting-started.md0000644000175100017510000001323715136437134017437 0ustar00runnerrunner# Getting Started This guide will walk you through the basics of using pyNetBox to interact with NetBox. ## Basic Connection First, import pynetbox and create an API connection: ```python import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) ``` ### Connection Parameters The `api()` method accepts several parameters: - **url** (required): The base URL of your NetBox instance - **token** (optional): API authentication token (required for write operations) - **threading** (optional): Enable multithreaded requests (default: `False`) - **strict_filters** (optional): Enable filter validation (default: `False`) ```python nb = pynetbox.api( 'http://localhost:8000', token='your-token-here', threading=True, strict_filters=True ) ``` ## Understanding the API Structure PyNetBox mirrors NetBox's app structure. NetBox apps become attributes of the API object: ```python nb.dcim # Data Center Infrastructure Management nb.ipam # IP Address Management nb.circuits # Circuit management nb.virtualization # Virtual machines and clusters nb.tenancy # Tenants and contacts nb.extras # Tags, custom fields, etc. nb.users # Users and permissions nb.wireless # Wireless LANs and links nb.core # Core objects (data sources, jobs) nb.vpn # VPN tunnels and terminations ``` Each app has endpoints that correspond to NetBox's API endpoints: ```python nb.dcim.devices nb.dcim.sites nb.dcim.racks nb.ipam.ip_addresses nb.ipam.prefixes # ... and so on ``` ## Querying Data ### Getting All Objects Use `.all()` to retrieve all objects from an endpoint: ```python # Get all devices devices = nb.dcim.devices.all() for device in devices: print(device.name) ``` !!! warning "Generator Objects" The `.all()` and `.filter()` methods return generators that can only be iterated once. To iterate multiple times, wrap the result in a `list()`: ```python devices = list(nb.dcim.devices.all()) ``` ### Filtering Objects Use `.filter()` to query specific objects: ```python # Get all devices with a specific role leaf_switches = nb.dcim.devices.filter(role='leaf-switch') # Multiple filters devices = nb.dcim.devices.filter( site='headquarters', status='active', role='access-switch' ) # Filter by custom fields devices = nb.dcim.devices.filter(cf_environment='production') ``` ### Getting a Single Object Use `.get()` to retrieve a specific object: ```python # Get by ID device = nb.dcim.devices.get(1) # Get by name device = nb.dcim.devices.get(name='spine1') # Get returns None if not found device = nb.dcim.devices.get(name='nonexistent') if device is None: print("Device not found") ``` ## Working with Objects ### Accessing Attributes Objects return attributes as properties: ```python device = nb.dcim.devices.get(1) print(device.name) print(device.serial) print(device.device_type) print(device.site.name) # Nested objects ``` ### Checking Available Attributes ```python device = nb.dcim.devices.get(1) # Convert to dict to see all attributes print(dict(device)) # Or access the raw data print(device.serialize()) ``` ## Creating Objects Use `.create()` to create new objects: ```python # Create a new site new_site = nb.dcim.sites.create( name='new-datacenter', slug='new-datacenter', status='planned' ) # Create a device new_device = nb.dcim.devices.create( name='new-switch', device_type=1, # Can use ID site=new_site.id, # Or reference the created object device_role=5 ) # Create with nested data new_ip = nb.ipam.ip_addresses.create( address='10.0.0.1/24', status='active', assigned_object_type='dcim.interface', assigned_object_id=123 ) ``` ## Updating Objects There are two ways to update objects: ### Method 1: Update and Save ```python device = nb.dcim.devices.get(1) device.serial = 'ABC123' device.asset_tag = 'ASSET001' device.save() ``` ### Method 2: Using Update ```python device = nb.dcim.devices.get(1) device.update({ 'serial': 'ABC123', 'asset_tag': 'ASSET001' }) ``` ## Deleting Objects Use `.delete()` to remove objects: ```python device = nb.dcim.devices.get(1) device.delete() # Or delete directly by ID nb.dcim.devices.delete(1) ``` ## Working with Choices Get available choices for choice fields: ```python # Get all status choices for devices statuses = nb.dcim.devices.choices() print(statuses['status']) # Get choices for a specific field interface_types = nb.dcim.interfaces.choices() print(interface_types['type']) ``` ## Pagination NetBox paginates results by default. PyNetBox handles pagination automatically: ```python # This will automatically fetch all pages devices = nb.dcim.devices.all() # You can also limit results devices = nb.dcim.devices.filter(limit=10) ``` ## Error Handling ```python from pynetbox.core.query import RequestError, ContentError try: device = nb.dcim.devices.create( name='test-device', device_type=1, site=1, device_role=1 ) except RequestError as e: print(f"Request failed: {e}") except ContentError as e: print(f"Content error: {e}") ``` ## Next Steps - Review the [API Reference](api.md) for detailed documentation on core classes - Learn about [Threading](advanced.md#threading) for faster queries - Explore [Filter Validation](advanced.md#filter-validation) for safer queries - Review special methods documentation: - [DCIM Special Methods](dcim.md) - [IPAM Special Methods](ipam.md) - [Virtualization Special Methods](virtualization.md) - Check out [Advanced Topics](advanced.md) for custom sessions and branching - Refer to [NetBox API Documentation](https://demo.netbox.dev/api/docs/) for standard CRUD operations ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/index.md0000644000175100017510000000514615136437134015441 0ustar00runnerrunner# PyNetBox Documentation Python API client library for [NetBox](https://github.com/netbox-community/netbox). ## Overview PyNetBox is a Python client library that provides a simple and intuitive interface to interact with the NetBox REST API. It abstracts the complexity of making HTTP requests and provides a Pythonic way to work with NetBox data. ## Features - **Intuitive API**: Access NetBox endpoints through simple Python attributes - **Full CRUD Support**: Create, read, update, and delete NetBox objects - **Threading Support**: Parallel requests for improved performance on large queries - **Filter Validation**: Optional strict validation of filters against NetBox's OpenAPI spec - **Custom Sessions**: Support for custom HTTP sessions with SSL, timeouts, and retries - **Branch Support**: Context manager for NetBox branching plugin - **Comprehensive Coverage**: Support for all NetBox apps (DCIM, IPAM, Circuits, Virtualization, etc.) ## NetBox Version Compatibility !!! warning "Version Requirements" Version 6.7 and later of pyNetBox only supports NetBox 3.3 and above. Each pyNetBox version has been tested with its corresponding NetBox version: | NetBox Version | PyNetBox Version | |:--------------:|:----------------:| | 4.5 | 7.6.0 | | 4.4 | 7.5.0 | | 4.3 | 7.5.0 | | 4.2 | 7.5.0 | | 4.1 | 7.5.0 | | 4.0.6 | 7.4.1 | | 4.0.0 | 7.3.4 | | 3.7 | 7.3.0 | | 3.6 | 7.2.0 | | 3.5 | 7.1.0 | | 3.3 | 7.0.0 | ## Quick Example ```python import pynetbox # Initialize the API connection nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) # Query all devices devices = nb.dcim.devices.all() for device in devices: print(device.name) # Filter devices leaf_switches = nb.dcim.devices.filter(role='leaf-switch') # Get a specific device device = nb.dcim.devices.get(name='spine1') # Create a new device new_device = nb.dcim.devices.create( name='new-device', device_type=1, site=1, device_role=1 ) # Update a device device.serial = 'ABC123' device.save() ``` ## Getting Help - **GitHub Issues**: Report bugs or request features at [github.com/netbox-community/pynetbox/issues](https://github.com/netbox-community/pynetbox/issues) - **Documentation**: Full API reference and guides available in this documentation - **Source Code**: Available at [github.com/netbox-community/pynetbox](https://github.com/netbox-community/pynetbox) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/installation.md0000644000175100017510000000461715136437134017035 0ustar00runnerrunner# Installation ## Requirements - **Python**: 3.10 or higher - **NetBox**: 3.3 or higher (for pyNetBox 6.7+) - **Dependencies**: - `requests>=2.20.0,<3.0` - `packaging` ## Installation Methods ### Install from PyPI (Recommended) The easiest way to install pyNetBox is using pip: ```bash pip install pynetbox ``` ### Install from Source If you need the latest development version or want to contribute: ```bash # Clone the repository git clone https://github.com/netbox-community/pynetbox.git cd pynetbox # Install in development mode pip install -e . # Or install directly python setup.py install ``` ### Using a Virtual Environment (Recommended) It's recommended to use a virtual environment to isolate pyNetBox dependencies: ```bash # Create a virtual environment python3 -m venv venv # Activate it (Linux/macOS) source venv/bin/activate # Activate it (Windows) venv\Scripts\activate # Install pynetbox pip install pynetbox ``` ## Verifying Installation After installation, verify pyNetBox is installed correctly: ```python import pynetbox print(pynetbox.version) ``` You can also test connectivity to your NetBox instance: ```python import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='your-api-token-here' ) # Check NetBox version print(nb.version) # Check API status print(nb.status()) ``` ## Upgrading To upgrade to the latest version: ```bash pip install --upgrade pynetbox ``` To upgrade to a specific version: ```bash pip install pynetbox==7.6.0 ``` ## Development Installation If you're planning to develop or contribute to pyNetBox: ```bash # Clone the repository git clone https://github.com/netbox-community/pynetbox.git cd pynetbox # Create and activate virtual environment python3 -m venv venv source venv/bin/activate # Install development dependencies pip install -r requirements.txt pip install -r requirements-dev.txt # Install in editable mode pip install -e . ``` ## Docker Setup for Testing The test suite requires Docker for integration tests: ```bash # Ensure Docker is installed and running docker --version # Run the test suite pytest ``` For more information on running tests, see the [Development Guide](development/index.md). ## Next Steps - Read the [Getting Started Guide](getting-started.md) for basic usage - Explore the [API Reference](endpoint.md) for detailed documentation - Check the [Advanced Topics](advanced.md) for custom configurations ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/release-notes.md0000644000175100017510000001116115136437134017072 0ustar00runnerrunner# Release Notes ## Version 7.6.1 (January 28, 2026) #### Enhancements - [#726](https://github.com/netbox-community/pynetbox/issues/726) - Use `dict` instead of `OrderedDict` in Record serialization #### New Features - [#434](https://github.com/netbox-community/pynetbox/issues/434) - Add cable path tracing support for front ports, rear ports, and virtual circuit terminations #### Bug Fixes - [#586](https://github.com/netbox-community/pynetbox/issues/586) - Update internal object state after save operations to prevent attribute reset issues --- ## Version 7.6.0 (January 9, 2026) #### Breaking Changes - **ObjectChange** moved to `core` module for NetBox 4.1.0+ compatibility - Previously located in `extras.object_changes` - Now accessible via `nb.core.object_changes` #### New Features - Support for v2 Tokens introduced in NetBox 4.5.0 - Enhanced token management capabilities #### Enhancements - Added SVG support for Rack Elevation endpoint - Rack elevation diagrams can now be rendered as SVG - Access via `rack.elevation.list(render='svg')` #### Bug Fixes - Fixed token authentication when retrieving NetBox version - Prevents 403 errors during version checks - Token now properly included in version API requests #### Compatibility - Supports NetBox 4.5 --- ## Version 7.5.0 (May 20, 2024) #### Enhancements - Expanded cable trace functionality to include: - CircuitTerminations - ConsolePorts - ConsoleServerPorts - PowerOutlets - PowerPorts - Added built-in function to activate a branch - Use `nb.activate_branch()` context manager for NetBox branching plugin #### Bug Fixes - Fixed choices returned when API tokens have PUT but not POST permissions - Fixed `nb.version` property when using OIDC proxy authentication #### Compatibility - Supports NetBox 4.1, 4.2, 4.3, 4.4 --- ## Version 7.4.1 (October 25, 2024) #### Security - Updated `requests` and `urllib3` Python libraries to address security vulnerabilities - No functional changes --- ## Version 7.4.0 (August 8, 2024) #### Enhancements - Added initial NetBox 4.0 support - Added Python 3.12 support #### Bug Fixes - Fixed complex custom_fields insertion failures - Replaced `None` with `'null'` in query parameters - Corrected connected endpoints behavior #### Testing - Removed Python 3.8 and 3.9 from test matrix - Added Python 3.12 to CI/CD pipeline #### Compatibility - Supports NetBox 4.0.6+ --- ## Version 7.3.4 (July 2, 2024) #### Bug Fixes - Fixed API version detection for NetBox versions exceeding 4.x - Removed linting errors #### Testing - Dropped NetBox 3.3 from test matrix - Focus on NetBox 4.x support --- ## Version 7.3.3 (January 5, 2024) #### Fixes - PyPI release fix - No functional changes --- ## Version 7.3.2 (January 4, 2024) #### Fixes - Fixed setup.py for new publish workflow - No functional changes --- ## Version 7.3.1 (January 4, 2024) #### Fixes - Updated PyPI publish workflow - No functional changes --- ## Version 7.3.0 (January 3, 2024) #### New Features - Added NetBox v3.7 support #### Dependencies - Added `pyyaml` dependency #### Testing - Updated test suite for NetBox 3.7 compatibility #### Compatibility - Supports NetBox 3.7 --- ## Version 7.2.0 (September 7, 2023) #### New Features - Added NetBox v3.6 support #### Compatibility - Supports NetBox 3.6 --- ## Version 7.1.0 (August 2023) #### Code Quality - Lint fixes and code cleanup - Improved code formatting consistency #### Compatibility - Supports NetBox 3.5 --- ## Version 7.0.1 (June 2023) #### Enhancements - Updated code formatting - Documentation improvements --- ## Version 7.0.0 (June 2023) #### Breaking Changes - **Minimum NetBox version**: 3.3+ - Removed support for NetBox versions below 3.3 #### New Features - Full NetBox 3.3 API support - Updated test suite for NetBox 3.3 #### Testing - Completely overhauled test scripts - Updated test settings for NetBox 3.3 compatibility - Improved integration test coverage #### Compatibility - **NetBox 3.3+ required** - Python 3.8+ required --- ## Version 6.7.x and Earlier For release notes for versions 6.7 and earlier, please refer to the [GitHub Releases page](https://github.com/netbox-community/pynetbox/releases). Key highlights from earlier versions: #### Version 6.7 (Last pre-7.0 release) - Last version to support NetBox < 3.3 - Python 3.7+ support #### Version 6.6 - Custom error pickling fixes - VLAN __str__ improvements - VirtualChassis enhancements #### Version 6.5 - Added available-vlans support for VLAN groups #### Version 6.4 - Added Wireless app support - Data returned as dict improvements #### Version 6.3 - Field name lookup improvements - Enhanced filtering capabilities ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/request.md0000644000175100017510000000044715136437134016021 0ustar00runnerrunner# Request ::: pynetbox.core.query.RequestError handler: python options: members: false ::: pynetbox.core.query.ContentError handler: python options: members: false ::: pynetbox.core.query.AllocationError handler: python options: members: false ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/response.md0000644000175100017510000000077715136437134016175 0ustar00runnerrunner# Response ::: pynetbox.core.response.Record handler: python options: members: - delete - full_details - save - serialize - update - updates show_source: true show_root_heading: true ::: pynetbox.core.response.RecordSet handler: python options: members: true options: members: - delete - update show_source: true show_root_heading: true ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.7973304 pynetbox-7.6.1/docs/stylesheets/0000755000175100017510000000000015136437144016357 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/stylesheets/extra.css0000644000175100017510000000356015136437134020217 0ustar00runnerrunner/* Custom styles for pynetbox documentation */ /* Improve code block appearance */ .md-typeset pre > code { padding: 1em; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* Style method signatures */ .md-typeset .doc-contents .doc-heading { border-bottom: 1px solid var(--md-default-fg-color--lightest); padding-bottom: 0.5em; margin-bottom: 1em; } /* Improve method documentation */ .md-typeset .doc-contents .doc-heading + p { margin-top: 1em; font-size: 0.9em; color: var(--md-default-fg-color--light); } /* Style parameters and returns sections */ .md-typeset .doc-contents .doc-heading + p + h3 { margin-top: 1.5em; font-size: 1.1em; color: var(--md-primary-fg-color); } /* Improve table appearance */ .md-typeset table { border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .md-typeset table th { background-color: var(--md-primary-fg-color); color: var(--md-primary-bg-color); } /* Style code examples */ .md-typeset .doc-contents .doc-heading + p + h3 + p + .highlight { margin: 1em 0; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* Improve navigation */ .md-nav__item .md-nav__link--active { color: var(--md-primary-fg-color); font-weight: bold; } /* Style admonitions */ .md-typeset .admonition { border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* Improve method visibility */ .md-typeset .doc-contents .doc-heading { scroll-margin-top: 100px; } /* Style method parameters */ .md-typeset .doc-contents .doc-heading + p + h3 + p + ul { margin-left: 1em; padding-left: 1em; border-left: 2px solid var(--md-primary-fg-color); } /* Improve code block readability */ .md-typeset code { padding: 0.2em 0.4em; border-radius: 3px; background-color: var(--md-code-bg-color); box-shadow: 0 1px 2px rgba(0,0,0,0.1); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/docs/virtualization.md0000644000175100017510000000145615136437134017416 0ustar00runnerrunner# Virtualization This page documents special methods available for Virtualization models in pyNetBox. !!! note "Standard API Operations" Standard CRUD operations (`.all()`, `.filter()`, `.get()`, `.create()`, `.update()`, `.delete()`) follow NetBox's REST API patterns. Refer to the [NetBox API documentation](https://demo.netbox.dev/api/docs/) for details on available endpoints and filters. ## Virtual Machines ### Config Rendering The `render_config` property renders virtual machine configuration based on config contexts and templates. ::: pynetbox.models.virtualization.VirtualMachines.render_config handler: python options: show_source: true **Example:** ```python vm = nb.virtualization.virtual_machines.get(name='web-vm-01') config = vm.render_config.create() print(config) ``` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/mkdocs.yml0000644000175100017510000000325215136437134015057 0ustar00runnerrunnersite_name: pynetbox site_description: Python API client library for NetBox theme: name: material features: - navigation.tabs - navigation.sections - navigation.expand - navigation.top - navigation.indexes - toc.follow - search.suggest - search.highlight - content.code.copy plugins: - search - mkdocstrings: default_handler: python handlers: python: options: show_source: true show_root_heading: true heading_level: 3 nav: - Home: index.md - Getting Started: - Installation: installation.md - Quick Start: getting-started.md - API Reference: - Core Classes: api.md - Endpoint: endpoint.md - Response: response.md - Request: request.md - Circuits: circuits.md - DCIM: dcim.md - IPAM: ipam.md - Virtualization: virtualization.md - Advanced Topics: - Advanced Usage: advanced.md - Branching: branching.md - Development: - Development Guide: development/index.md - Getting Started: development/getting-started.md - Release Checklist: development/release-checklist.md - Release Notes: release-notes.md extra_css: - stylesheets/extra.css markdown_extensions: - pymdownx.highlight: anchor_linenums: true line_spans: __span pygments_lang_class: true - pymdownx.inlinehilite - pymdownx.snippets - pymdownx.superfences - admonition - pymdownx.details - pymdownx.emoji: emoji_index: !!python/name:material.extensions.emoji.twemoji emoji_generator: !!python/name:material.extensions.emoji.to_svg - toc: permalink: true toc_depth: 3 - tables - attr_list - md_in_html ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.7973304 pynetbox-7.6.1/pynetbox/0000755000175100017510000000000015136437144014723 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/__init__.py0000644000175100017510000000060315136437134017032 0ustar00runnerrunnerfrom pynetbox.core.api import Api from pynetbox.core.query import ( AllocationError, ContentError, RequestError, ParameterValidationError, ) __version__ = "7.6.1" # Lowercase alias for backward compatibility api = Api __all__ = ( "Api", "AllocationError", "ContentError", "RequestError", "ParameterValidationError", "api", "__version__", ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.7993305 pynetbox-7.6.1/pynetbox/core/0000755000175100017510000000000015136437144015653 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/core/__init__.py0000644000175100017510000000000015136437134017751 0ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/core/api.py0000644000175100017510000002203315136437134016775 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import contextlib import requests from pynetbox.core.app import App, PluginsApp from pynetbox.core.query import Request from pynetbox.core.response import Record class Api: """The API object is the point of entry to pynetbox. After instantiating the Api() with the appropriate named arguments you can specify which app and endpoint you wish to interact with. Valid attributes currently are: * circuits * core (NetBox 3.5+) * dcim * extras * ipam * tenancy * users * virtualization * vpn (NetBox 3.7+) * wireless Calling any of these attributes will return an `App` object which exposes endpoints as attributes. ## Additional Attributes * **http_session(requests.Session)**: Override the default session with your own. This is used to control a number of HTTP behaviors such as SSL verification, custom headers, retires, and timeouts. See [custom sessions](advanced.md#custom-sessions) for more info. ## Parameters * **url** (str): The base URL to the instance of NetBox you wish to connect to. * **token** (str): Your NetBox token. * **threading** (bool, optional): Set to True to use threading in `.all()` and `.filter()` requests. ## Raises * **AttributeError**: If app doesn't exist. ## Examples ```python import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) list(nb.dcim.devices.all()) # [test1-leaf1, test1-leaf2, test1-leaf3] ``` """ def __init__( self, url, token=None, threading=False, strict_filters=False, ): """Initialize the API client. Args: url (str): The base URL to the instance of NetBox you wish to connect to. token (str, optional): Your NetBox API token. If not provided, authentication will be required for each request. threading (bool, optional): Set to True to use threading in `.all()` and `.filter()` requests, defaults to False. strict_filters (bool, optional): Set to True to check GET call filters against OpenAPI specifications (intentionally not done in NetBox API), defaults to False. """ base_url = "{}/api".format(url if url[-1] != "/" else url[:-1]) self.token = token self.base_url = base_url self.http_session = requests.Session() self.threading = threading self.strict_filters = strict_filters # Initialize NetBox apps self.circuits = App(self, "circuits") self.core = App(self, "core") self.dcim = App(self, "dcim") self.extras = App(self, "extras") self.ipam = App(self, "ipam") self.tenancy = App(self, "tenancy") self.users = App(self, "users") self.virtualization = App(self, "virtualization") self.vpn = App(self, "vpn") self.wireless = App(self, "wireless") self.plugins = PluginsApp(self) @property def version(self): """Gets the API version of NetBox. Can be used to check the NetBox API version if there are version-dependent features or syntaxes in the API. ## Returns Version number as a string. ## Example ```python import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) nb.version # '3.1' ``` """ version = Request( base=self.base_url, token=self.token, http_session=self.http_session, ).get_version() return version def openapi(self): """Returns the OpenAPI spec. Quick helper function to pull down the entire OpenAPI spec. It is stored in memory to avoid repeated calls on NetBox API. ## Returns dict: The OpenAPI specification as a dictionary. ## Example ```python import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) nb.openapi() # {...} ``` """ if not (openapi := getattr(self, "_openapi", None)): openapi = self._openapi = Request( base=self.base_url, http_session=self.http_session, ).get_openapi() return openapi def status(self): """Gets the status information from NetBox. ## Returns Dictionary containing NetBox status information. ## Raises `RequestError`: If the request is not successful. ## Example ```python from pprint import pprint pprint(nb.status()) { 'django-version': '3.1.3', 'installed-apps': { 'cacheops': '5.0.1', 'debug_toolbar': '3.1.1', 'django_filters': '2.4.0', 'django_prometheus': '2.1.0', 'django_rq': '2.4.0', 'django_tables2': '2.3.3', 'drf_yasg': '1.20.0', 'mptt': '0.11.0', 'rest_framework': '3.12.2', 'taggit': '1.3.0', 'timezone_field': '4.0' }, 'netbox-version': '2.10.2', 'plugins': {}, 'python-version': '3.7.3', 'rq-workers-running': 1 } ``` """ status = Request( base=self.base_url, token=self.token, http_session=self.http_session, ).get_status() return status def create_token(self, username, password): """Creates an API token using a valid NetBox username and password. Saves the created token automatically in the API object. ## Parameters * **username** (str): NetBox username * **password** (str): NetBox password ## Returns `Record`: The token as a Record object. ## Raises `RequestError`: If the request is not successful. ## Example ```python import pynetbox nb = pynetbox.api("https://netbox-server") token = nb.create_token("admin", "netboxpassword") nb.token # '96d02e13e3f1fdcd8b4c089094c0191dcb045bef' from pprint import pprint pprint(dict(token)) { 'created': '2021-11-27T11:26:49.360185+02:00', 'description': '', 'display': '045bef (admin)', 'expires': None, 'id': 2, 'key': '96d02e13e3f1fdcd8b4c089094c0191dcb045bef', 'url': 'https://netbox-server/api/users/tokens/2/', 'user': { 'display': 'admin', 'id': 1, 'url': 'https://netbox-server/api/users/users/1/', 'username': 'admin' }, 'write_enabled': True } ``` """ resp = Request( base="{}/users/tokens/provision/".format(self.base_url), http_session=self.http_session, ).post(data={"username": username, "password": password}) # Save the newly created API token, otherwise populating the Record # object details will fail self.token = resp["key"] return Record(resp, self, None) @contextlib.contextmanager def activate_branch(self, branch): """Context manager to activate the branch by setting the schema ID in the headers. **Note**: The NetBox branching plugin must be installed and enabled in your NetBox instance for this functionality to work. ## Parameters * **branch** (Record): The NetBox branch to activate ## Raises `ValueError`: If the branch is not a valid NetBox branch. ## Example ```python import pynetbox nb = pynetbox.api("https://netbox-server") branch = nb.plugins.branching.branches.create(name="testbranch") with nb.activate_branch(branch): sites = nb.dcim.sites.all() # All operations within this block will use the branch's schema ``` """ if not isinstance(branch, Record) or "schema_id" not in dict(branch): raise ValueError( f"The specified branch is not a valid NetBox branch: {branch}." ) self.http_session.headers["X-NetBox-Branch"] = branch.schema_id try: yield finally: self.http_session.headers.pop("X-NetBox-Branch", None) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/core/app.py0000644000175100017510000000763215136437134017014 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from pynetbox.core.endpoint import Endpoint from pynetbox.core.query import Request from pynetbox.models import ( circuits, core, dcim, extras, ipam, users, virtualization, wireless, ) class App: """Represents apps in NetBox. Calls to attributes are returned as Endpoint objects. ## Returns Endpoint matching requested attribute. ## Raises RequestError if requested endpoint doesn't exist. """ def __init__(self, api, name): self.api = api self.name = name self._setmodel() models = { "circuits": circuits, "core": core, "dcim": dcim, "extras": extras, "ipam": ipam, "users": users, "virtualization": virtualization, "wireless": wireless, } def _setmodel(self): self.model = App.models[self.name] if self.name in App.models else None def __getstate__(self): return {"api": self.api, "name": self.name} def __setstate__(self, d): self.__dict__.update(d) self._setmodel() def __getattr__(self, name): return Endpoint(self.api, self, name, model=self.model) def config(self): """Returns config response from app. ## Returns Raw response from NetBox's config endpoint. ## Raises RequestError if called for an invalid endpoint. ## Examples ```python pprint.pprint(nb.users.config()) { 'tables': { 'DeviceTable': { 'columns': [ 'name', 'status', 'tenant', 'role', 'site', 'primary_ip', 'tags' ] } } } ``` """ config = Request( base="{}/{}/config/".format( self.api.base_url, self.name, ), token=self.api.token, http_session=self.api.http_session, ).get() return config class PluginsApp: """Basically valid plugins api could be handled by same App class, but you need to add plugins to request url path. ## Returns App with added plugins into path. """ def __init__(self, api): self.api = api def __getstate__(self): return self.__dict__ def __setstate__(self, d): self.__dict__.update(d) def __getattr__(self, name): return App(self.api, "plugins/{}".format(name.replace("_", "-"))) def installed_plugins(self): """Returns raw response with installed plugins. ## Returns Raw response NetBox's installed plugins. ## Examples ```python nb.plugins.installed_plugins() [ { 'name': 'test_plugin', 'package': 'test_plugin', 'author': 'Dmitry', 'description': 'Netbox test plugin', 'verison': '0.10' } ] ``` """ installed_plugins = Request( base="{}/plugins/installed-plugins".format( self.api.base_url, ), token=self.api.token, http_session=self.api.http_session, ).get() return installed_plugins ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/core/endpoint.py0000644000175100017510000005424015136437134020051 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from pynetbox.core.query import Request, RequestError, ParameterValidationError from pynetbox.core.response import Record, RecordSet RESERVED_KWARGS = () class Endpoint: """Represent actions available on endpoints in the Netbox API. Takes ``name`` and ``app`` passed from App() and builds the correct url to make queries to and the proper Response object to return results in. ## Parameters * **api** (Api): Takes Api created at instantiation. * **app** (App): Takes App. * **name** (str): Name of endpoint passed to App(). * **model** (obj, optional): Custom model for given app. ## Note In order to call NetBox endpoints with dashes in their names you should convert the dash to an underscore. (E.g. querying the ip-addresses endpoint is done with ``nb.ipam.ip_addresses.all()``.) """ def __init__(self, api, app, name, model=None): self.return_obj = self._lookup_ret_obj(name, model) self.name = name.replace("_", "-") self.api = api self.app = app self.base_url = api.base_url self.token = api.token self.url = "{base_url}/{app}/{endpoint}".format( base_url=self.base_url, app=app.name, endpoint=self.name, ) self._choices = None def _lookup_ret_obj(self, name, model): """Loads unique Response objects. This method loads a unique response object for an endpoint if it exists. Otherwise return a generic `Record` object. ## Parameters * **name** (str): Endpoint name. * **model** (obj): The application model that contains unique Record objects. ## Returns Record (obj) """ if model: name = name.title().replace("_", "") ret = getattr(model, name, Record) else: ret = Record return ret def _validate_openapi_parameters(self, method: str, parameters: dict) -> None: """Validate GET request parameters against OpenAPI specification This method raises a **ParameterValidationError** if parameters passed to NetBox API do not match the OpenAPI specification or validation fails. ## Parameters * **method** : Only "get" is supported as for other methods NetBox already does proper validation * **parameters** : kwargs passed to filter() method ## Returns None """ if method.lower() != "get": raise RuntimeError(f"Unsupported method '{method}'.") openapi_definition_path = "/api/{app}/{endpoint}/".format( app=self.app.name, endpoint=self.name, ) # Parse NetBox OpenAPI definition try: openapi_definition = self.api.openapi()["paths"].get( openapi_definition_path ) if not openapi_definition: raise ParameterValidationError( f"Path '{openapi_definition_path}' does not exist in NetBox OpenAPI specification." ) openapi_parameters = openapi_definition[method]["parameters"] allowed_parameters = [p["name"] for p in openapi_parameters] except KeyError as exc: raise ParameterValidationError( f"Error while parsing Netbox OpenAPI specification: {exc}" ) # Validate all parameters validation_errors = [] for p in parameters: if p not in allowed_parameters: validation_errors.append( f"'{p}' is not allowed as parameter on path '{openapi_definition_path}'." ) if len(validation_errors) > 0: raise ParameterValidationError(validation_errors) def all(self, limit=0, offset=None): """Queries the 'ListView' of a given endpoint. Returns all objects from an endpoint. ## Parameters * **limit** (int, optional): Overrides the max page size on paginated returns. This defines the number of records that will be returned with each query to the Netbox server. The queries will be made as you iterate through the result set. * **offset** (int, optional): Overrides the offset on paginated returns. ## Returns A RecordSet object. ## Examples ```python devices = list(nb.dcim.devices.all()) for device in devices: print(device.name) # test1-leaf1 # test1-leaf2 # test1-leaf3 ``` If you want to iterate over the results multiple times then encapsulate them in a list like this: ```python devices = list(nb.dcim.devices.all()) ``` This will cause the entire result set to be fetched from the server. """ if limit == 0 and offset is not None: raise ValueError("offset requires a positive limit value") req = Request( base="{}/".format(self.url), token=self.token, http_session=self.api.http_session, threading=self.api.threading, limit=limit, offset=offset, ) return RecordSet(self, req) def get(self, *args, **kwargs): """Queries the DetailsView of a given endpoint. ## Parameters * **key** (int, optional): id for the item to be retrieved. * **kwargs**: Accepts the same keyword args as filter(). Any search argument the endpoint accepts can be added as a keyword arg. * **strict_filters** (bool, optional): Overrides the global filter validation per-request basis. Handled by the filter() method. ## Returns A single Record object or None ## Raises ValueError: if kwarg search return more than one value. ## Examples Referencing with a kwarg that only returns one value: ```python nb.dcim.devices.get(name='test1-a3-tor1b') # test1-a3-tor1b ``` Referencing with an id: ```python nb.dcim.devices.get(1) # test1-edge1 ``` Using multiple named arguments. For example, retrieving the location when the location name is not unique and used in multiple sites: ```python nb.locations.get(site='site-1', name='Row 1') # Row 1 ``` """ try: key = args[0] except IndexError: key = None if not key: resp = self.filter(**kwargs) ret = next(resp, None) if not ret: return ret try: next(resp) raise ValueError( "get() returned more than one result. " "Check that the kwarg(s) passed are valid for this " "endpoint or use filter() or all() instead." ) except StopIteration: return ret req = Request( key=key, base=self.url, token=self.token, http_session=self.api.http_session, ) try: return next(RecordSet(self, req), None) except RequestError as e: if e.req.status_code == 404: return None else: raise e def filter(self, *args, **kwargs): """Queries the 'ListView' of a given endpoint. Takes named arguments that match the usable filters on a given endpoint. If an argument is passed then it's used as a freeform search argument if the endpoint supports it. ## Parameters * **args** (str, optional): Freeform search string that's accepted on given endpoint. * **kwargs** (str, optional): Any search argument the endpoint accepts can be added as a keyword arg. * **limit** (int, optional): Overrides the max page size on paginated returns. This defines the number of records that will be returned with each query to the Netbox server. The queries will be made as you iterate through the result set. * **offset** (int, optional): Overrides the offset on paginated returns. * **strict_filters** (bool, optional): Overrides the global filter validation per-request basis. ## Returns A RecordSet object. ## Examples To return a list of objects matching a named argument filter: ```python devices = nb.dcim.devices.filter(role='leaf-switch') for device in devices: print(device.name) # test1-leaf1 # test1-leaf2 # test1-leaf3 ``` ```python devices = nb.dcim.devices.filter(site='site-1') for device in devices: print(device.name) # test1-a2-leaf1 # test2-a2-leaf2 ``` ## Note If a keyword argument is incorrect a `TypeError` will not be returned by pynetbox. Instead, pynetbox will return all records filtered up to the last correct keyword argument. For example, if we used `site="Site 1"` instead of `site=site-1` when using filter on the devices endpoint, then pynetbox will return **all** devices across all sites instead of devices at Site 1. Using a freeform query along with a named argument: ```python devices = nb.dcim.devices.filter('a3', role='leaf-switch') for device in devices: print(device.name) # test1-a3-leaf1 # test1-a3-leaf2 ``` """ if args: kwargs.update({"q": args[0]}) if any(i in RESERVED_KWARGS for i in kwargs): raise ValueError( "A reserved kwarg was passed ({}). Please remove it " "and try again.".format(RESERVED_KWARGS) ) limit = kwargs.pop("limit") if "limit" in kwargs else 0 offset = kwargs.pop("offset") if "offset" in kwargs else None strict_filters = ( # kwargs value takes precedence on globally set value kwargs.pop("strict_filters") if "strict_filters" in kwargs else self.api.strict_filters ) if limit == 0 and offset is not None: raise ValueError("offset requires a positive limit value") filters = {x: y if y is not None else "null" for x, y in kwargs.items()} if strict_filters: self._validate_openapi_parameters("get", filters) req = Request( filters=filters, base=self.url, token=self.token, http_session=self.api.http_session, threading=self.api.threading, limit=limit, offset=offset, ) return RecordSet(self, req) def create(self, *args, **kwargs): """Creates an object on an endpoint. Takes named arguments that match the given endpoint's available fields. Returns a new object. ## Parameters * **args**: Not used. * **kwargs**: Fields and values to create the object with. ## Returns A Record object. ## Examples Creating a new device: ```python new_device = nb.dcim.devices.create( name='test-device', device_type=1, device_role=1, site=1 ) ``` Creating a new device with a nested object: ```python new_device = nb.dcim.devices.create( name='test-device', device_type={'id': 1}, device_role={'id': 1}, site={'id': 1} ) ``` """ req = Request( base=self.url, token=self.token, http_session=self.api.http_session, ).post(args[0] if args else kwargs) if isinstance(req, list): return [self.return_obj(i, self.api, self) for i in req] return self.return_obj(req, self.api, self) def update(self, objects): """Updates objects in NetBox. Takes a list of objects and updates them in NetBox. ## Parameters * **objects** (list): A list of Record objects to update. ## Returns A list of Record objects. ## Examples ```python devices = nb.dcim.devices.filter(site='test1') for device in devices: device.status = 'active' nb.dcim.devices.update(devices) ``` """ series = [] if not isinstance(objects, list): raise ValueError( "Objects passed must be list[dict|Record] - was {}".format( type(objects) ) ) for o in objects: if isinstance(o, Record): data = o.updates() if data: data["id"] = o.id series.append(data) elif isinstance(o, dict): if "id" not in o: raise ValueError("id is missing from object: " + str(o)) series.append(o) else: raise ValueError( "Object passed must be dict|Record - was {}".format(type(objects)) ) req = Request( base=self.url, token=self.token, http_session=self.api.http_session, ).patch(series) if isinstance(req, list): return [self.return_obj(i, self.api, self) for i in req] return self.return_obj(req, self.api, self) def delete(self, objects): """Deletes objects from NetBox. Takes a list of objects and deletes them from NetBox. ## Parameters * **objects** (list): A list of Record objects to delete. ## Returns True if the delete operation was successful. ## Examples ```python devices = nb.dcim.devices.filter(site='test1') nb.dcim.devices.delete(devices) ``` """ cleaned_ids = [] if not isinstance(objects, list) and not isinstance(objects, RecordSet): raise ValueError( "objects must be list[str|int|Record]" "|RecordSet - was " + str(type(objects)) ) for o in objects: if isinstance(o, int): cleaned_ids.append(o) elif isinstance(o, str) and o.isnumeric(): cleaned_ids.append(int(o)) elif isinstance(o, Record): if not hasattr(o, "id"): raise ValueError( "Record from '" + o.url + "' does not have an id and cannot be bulk deleted" ) cleaned_ids.append(o.id) else: raise ValueError( "Invalid object in list of objects to delete: " + str(type(o)) ) req = Request( base=self.url, token=self.token, http_session=self.api.http_session, ) return True if req.delete(data=[{"id": i} for i in cleaned_ids]) else False def choices(self): """Returns all choices from the endpoint if it has them. ## Returns Dictionary of available choices. ## Examples ```python choices = nb.dcim.devices.choices() print(choices['status']) { 'label': 'Active', 'value': 'active' } ``` """ if self._choices: return self._choices req = Request( base=self.url, token=self.api.token, http_session=self.api.http_session, ).options() actions = req.get("actions", {}) post_data = actions.get("POST") or actions.get("PUT") if post_data is None: raise ValueError( "Unexpected format in the OPTIONS response at {}".format(self.url) ) self._choices = {} for prop in post_data: if "choices" in post_data[prop]: self._choices[prop] = post_data[prop]["choices"] return self._choices def count(self, *args, **kwargs): """Returns the count of objects in a query. Takes named arguments that match the usable filters on a given endpoint. If an argument is passed then it's used as a freeform search argument if the endpoint supports it. ## Parameters * **args** (str, optional): Freeform search string that's accepted on given endpoint. * **kwargs** (str, optional): Any search argument the endpoint accepts can be added as a keyword arg. ## Returns Integer of count of objects. ## Examples ```python nb.dcim.devices.count(site='test1') # 27 ``` """ if args: kwargs.update({"q": args[0]}) if any(i in RESERVED_KWARGS for i in kwargs): raise ValueError( "A reserved {} kwarg was passed. Please remove it " "try again.".format(RESERVED_KWARGS) ) ret = Request( filters=kwargs, base=self.url, token=self.token, http_session=self.api.http_session, ) return ret.get_count() class DetailEndpoint: """Enables read/write operations on detail endpoints. Endpoints like `available-ips` that are detail routes off traditional endpoints are handled with this class. """ def __init__(self, parent_obj, name, custom_return=None): self.parent_obj = parent_obj self.custom_return = custom_return self.url = "{}/{}/{}/".format(parent_obj.endpoint.url, parent_obj.id, name) self.request_kwargs = dict( base=self.url, token=parent_obj.api.token, http_session=parent_obj.api.http_session, ) def list(self, **kwargs): """The view operation for a detail endpoint. Returns the response from NetBox for a detail endpoint. ## Parameters * **kwargs**: Key/value pairs that get converted into URL parameters when passed to the endpoint. E.g. `.list(method='get_facts')` would be converted to `.../?method=get_facts`. ## Returns A Record object or list of Record objects created from data retrieved from NetBox. """ req = Request(**self.request_kwargs).get(add_params=kwargs) if self.custom_return: return [ self.custom_return( i, self.parent_obj.endpoint.api, self.parent_obj.endpoint ) for i in req ] return req def create(self, data=None): """The write operation for a detail endpoint. Creates objects on a detail endpoint in NetBox. ## Parameters * **data** (dict/list, optional): A dictionary containing the key/value pair of the items you're creating on the parent object. Defaults to empty dict which will create a single item with default values. ## Returns A Record object or list of Record objects created from data created in NetBox. """ data = data or {} req = Request(**self.request_kwargs).post(data) if self.custom_return: if isinstance(req, list): return [ self.custom_return( req_item, self.parent_obj.endpoint.api, self.parent_obj.endpoint ) for req_item in req ] else: return self.custom_return( req, self.parent_obj.endpoint.api, self.parent_obj.endpoint ) return req class RODetailEndpoint(DetailEndpoint): def create(self, data): raise NotImplementedError("Writes are not supported for this endpoint.") class ROMultiFormatDetailEndpoint(RODetailEndpoint): """Read-only detail endpoint supporting multiple response formats. Handles endpoints that return data in different formats based on query parameters. Supports both structured data (JSON) and raw formats (e.g., SVG). The endpoint inspects the 'render' parameter to determine response format: - No parameter or render='json': Returns structured JSON data - render='svg': Returns raw SVG content ## Examples ```python rack = nb.dcim.racks.get(123) rack.elevation.list() # Returns: list of rack unit objects rack.elevation.list(render='svg') # Returns: SVG string rack.elevation.list(render='json') # Returns: list of rack unit objects ``` """ def list(self, **kwargs): """Returns data in the requested format. ## Parameters * **kwargs**: Key/value pairs that get converted into URL parameters. Supports 'render' parameter for format selection. ## Returns - If render is non-JSON format: Raw content (string) - If render is 'json' or absent: Structured data (list/generator) """ # Check if non-JSON format requested render_format = kwargs.get("render") if render_format == "svg": # Pass expect_json=False for raw SVG response req = Request(**self.request_kwargs, expect_json=False).get( add_params=kwargs ) # Return raw content for non-JSON formats return next(req) if render_format != "json" and render_format is not None: raise ValueError(f"Unsupported render format: {render_format}") # Return structured JSON response via parent class return super().list(**kwargs) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/core/query.py0000644000175100017510000004532415136437134017401 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import concurrent.futures as cf import io import os import json from packaging import version # NetBox v2 token prefix (introduced in NetBox 4.5.0) TOKEN_PREFIX = "nbt_" def _is_v2_token(token): """Detect if a token is NetBox v2 format. V2 tokens (introduced in NetBox 4.5.0) have the format: nbt_. The nbt_ prefix is used for secrets detection. V1 tokens are simple strings without dots. Returns True if token is v2 format, False otherwise. """ if not token or not token.startswith(TOKEN_PREFIX): return False # Remove nbt_ prefix token_body = token[len(TOKEN_PREFIX) :] # V2 tokens contain a dot separating the ID from the secret return "." in token_body def _is_file_like(obj): if isinstance(obj, (str, bytes)): return False # Check if it's a standard library IO object OR has a callable read method return isinstance(obj, io.IOBase) or ( hasattr(obj, "read") and callable(getattr(obj, "read")) ) def _extract_files(data): """Extract file-like objects from data dict. Returns a tuple of (clean_data, files) where clean_data has file objects removed and files is a dict suitable for requests' files parameter. """ if not isinstance(data, dict): return data, None files = {} clean_data = {} for key, value in data.items(): if _is_file_like(value): # Format: (filename, file_obj, content_type) # Try to get filename from file object, fallback to key filename = getattr(value, "name", None) if filename: # Extract just the filename, not the full path filename = os.path.basename(filename) else: filename = key files[key] = (filename, value) elif isinstance(value, tuple) and len(value) >= 2 and _is_file_like(value[1]): # Already in (filename, file_obj) or (filename, file_obj, content_type) format files[key] = value else: clean_data[key] = value return clean_data, files if files else None def calc_pages(limit, count): """Calculate number of pages required for full results set.""" return int(count / limit) + (limit % count > 0) class RequestError(Exception): """Basic Request Exception. More detailed exception that returns the original requests object for inspection. Along with some attributes with specific details from the requests object. If return is json we decode and add it to the message. ## Examples ```python try: nb.dcim.devices.create(name="destined-for-failure") except pynetbox.RequestError as e: print(e.error) ``` """ def __init__(self, req): if req.status_code == 404: self.message = "The requested url: {} could not be found.".format(req.url) else: try: self.message = "The request failed with code {} {}: {}".format( req.status_code, req.reason, req.json() ) except ValueError: self.message = ( "The request failed with code {} {} but more specific " "details were not returned in json. Check the NetBox Logs " "or investigate this exception's error attribute.".format( req.status_code, req.reason ) ) super().__init__(self.message) self.req = req self.request_body = req.request.body self.base = req.url self.error = req.text def __str__(self): return self.message class AllocationError(Exception): """Allocation Exception. Used with available-ips/available-prefixes when there is no room for allocation and NetBox returns 409 Conflict. """ def __init__(self, req): super().__init__(req) self.req = req self.request_body = req.request.body self.base = req.url self.error = "The requested allocation could not be fulfilled." def __str__(self): return self.error class ContentError(Exception): """Content Exception. If the API URL does not point to a valid NetBox API, the server may return a valid response code, but the content is not json. This exception is raised in those cases. """ def __init__(self, req): super().__init__(req) self.req = req self.request_body = req.request.body self.base = req.url self.error = ( "The server returned invalid (non-json) data. Maybe not a NetBox server?" ) def __str__(self): return self.error class ParameterValidationError(Exception): """API parameter validation Exception. Raised when filter parameters do not match Netbox OpenAPI specification. ## Examples ```python try: nb.dcim.devices.filter(field_which_does_not_exist="destined-for-failure") except pynetbox.ParameterValidationError as e: print(e.error) ``` """ def __init__(self, errors): super().__init__(errors) self.error = f"The request parameter validation returned an error: {errors}" def __str__(self): return self.error class Request: """Creates requests to the Netbox API. Responsible for building the url and making the HTTP(S) requests to Netbox's API. ## Parameters * **base** (str): Base URL passed in api() instantiation. * **filters** (dict, optional): Contains key/value pairs that correlate to the filters a given endpoint accepts. In (e.g. /api/dcim/devices/?name='test') 'name': 'test' would be in the filters dict. """ def __init__( self, base, http_session, filters=None, limit=None, offset=None, key=None, token=None, threading=False, expect_json=True, ): """Instantiates a new Request object. ## Parameters * **base** (string): Base URL passed in api() instantiation. * **filters** (dict, optional): Contains key/value pairs that correlate to the filters a given endpoint accepts. In (e.g. /api/dcim/devices/?name='test') 'name': 'test' would be in the filters dict. * **key** (int, optional): Database id of the item being queried. * **expect_json** (bool, optional): If True, expects JSON response and sets appropriate Accept header. If False, expects raw content (e.g., SVG, XML) and returns text. Defaults to True. ## Note The `count` attribute is not initialized here. It is set dynamically by the `get()` method when paginating results, or by `get_count()` when explicitly requesting the count. This allows `get_count()` to use `hasattr()` to determine if a count has already been fetched. """ self.base = self.normalize_url(base) self.filters = filters or None self.key = key self.token = token self.http_session = http_session self.url = self.base if not key else "{}{}/".format(self.base, key) self.threading = threading self.limit = limit self.offset = offset self.expect_json = expect_json def get_openapi(self): """Gets the OpenAPI Spec.""" headers = { "Accept": "application/json", "Content-Type": "application/json", } current_version = version.parse(self.get_version()) if current_version >= version.parse("3.5"): req = self.http_session.get( "{}schema/".format(self.normalize_url(self.base)), headers=headers, ) else: req = self.http_session.get( "{}docs/?format=openapi".format(self.normalize_url(self.base)), headers=headers, ) if req.ok: return req.json() else: raise RequestError(req) def get_version(self): """Gets the API version of NetBox. Issues a GET request to the base URL to read the API version from the response headers. ## Returns Version number as a string. Empty string if version is not present in the headers. ## Raises RequestError if req.ok returns false. """ headers = {"Content-Type": "application/json"} self._add_auth_header(headers) req = self.http_session.get( self.normalize_url(self.base), headers=headers, ) if req.ok or req.status_code == 403: return req.headers.get("API-Version", "") else: raise RequestError(req) def get_status(self): """Gets the status from /api/status/ endpoint in NetBox. ## Returns Dictionary as returned by NetBox. ## Raises RequestError if request is not successful. """ headers = {"Content-Type": "application/json"} self._add_auth_header(headers) req = self.http_session.get( "{}status/".format(self.normalize_url(self.base)), headers=headers, ) if req.ok: return req.json() else: raise RequestError(req) def normalize_url(self, url): """Builds a url for POST actions.""" if url[-1] != "/": return "{}/".format(url) return url def _add_auth_header(self, headers): """Add authorization header to headers dict if token is present. ## Parameters * **headers** (dict): Headers dictionary to update with authorization. """ if self.token: if _is_v2_token(self.token): headers["authorization"] = "Bearer {}".format(self.token) else: headers["authorization"] = "Token {}".format(self.token) def _make_call(self, verb="get", url_override=None, add_params=None, data=None): # Extract any file-like objects from data files = None # Verbs that support request bodies with file uploads body_verbs = ("post", "put", "patch") # Set Accept header based on expected response type if self.expect_json: headers = {"accept": "application/json"} else: headers = {"accept": "*/*"} # Extract files from data for applicable verbs if data is not None and verb in body_verbs: data, files = _extract_files(data) # Set headers based on request type should_be_json_body = not files and ( verb in body_verbs or (verb == "delete" and data) ) if should_be_json_body: headers["Content-Type"] = "application/json" self._add_auth_header(headers) params = {} if not url_override: if self.filters: params.update(self.filters) if add_params: params.update(add_params) if files: # Use multipart/form-data for file uploads req = getattr(self.http_session, verb)( url_override or self.url, headers=headers, params=params, data=data, files=files, ) else: req = getattr(self.http_session, verb)( url_override or self.url, headers=headers, params=params, json=data ) if req.status_code == 409 and verb == "post": raise AllocationError(req) if verb == "delete": if req.ok: return True else: raise RequestError(req) elif req.ok: # Parse response based on expected type if self.expect_json: try: return req.json() except json.JSONDecodeError: raise ContentError(req) else: # Return raw text for non-JSON responses return req.text else: raise RequestError(req) def concurrent_get(self, ret, page_size, page_offsets): futures_to_results = [] with cf.ThreadPoolExecutor(max_workers=4) as pool: for offset in page_offsets: new_params = {"offset": offset, "limit": page_size} futures_to_results.append( pool.submit(self._make_call, add_params=new_params) ) for future in cf.as_completed(futures_to_results): result = future.result() ret.extend(result["results"]) def get(self, add_params=None): """Makes a GET request. Makes a GET request to NetBox's API, and automatically recurses any paginated results. ## Returns List of `Response` objects returned from the endpoint. ## Raises * RequestError if req.ok returns false. * ContentError if response is not json. """ if not add_params and self.limit is not None: add_params = {"limit": self.limit} if self.limit and self.offset is not None: # if non-zero limit and some offset -> add offset add_params["offset"] = self.offset req = self._make_call(add_params=add_params) if isinstance(req, dict) and req.get("results") is not None: self.count = req["count"] if self.offset is not None: # only yield requested page results if paginating for i in req["results"]: yield i elif self.threading: ret = req["results"] if req.get("next"): page_size = len(req["results"]) pages = calc_pages(page_size, req["count"]) page_offsets = [ increment * page_size for increment in range(1, pages) ] if pages == 1: req = self._make_call(url_override=req.get("next")) ret.extend(req["results"]) else: self.concurrent_get(ret, page_size, page_offsets) for i in ret: yield i else: first_run = True for i in req["results"]: yield i while req["next"]: # Not worrying about making sure add_params kwargs is # passed in here because results from detail routes aren't # paginated, thus far. if first_run: req = self._make_call( add_params={ "limit": self.limit or req["count"], "offset": len(req["results"]), } ) else: req = self._make_call(url_override=req["next"]) first_run = False for i in req["results"]: yield i elif isinstance(req, list): self.count = len(req) for i in req: yield i else: self.count = len(req) yield req def put(self, data): """Makes PUT request. Makes a PUT request to NetBox's API. ## Parameters * **data** (dict): Contains a dict that will be turned into a json object and sent to the API. ## Returns Dict containing the response from NetBox's API. ## Raises * RequestError if req.ok returns false. * ContentError if response is not json. """ return self._make_call(verb="put", data=data) def post(self, data): """Makes POST request. Makes a POST request to NetBox's API. ## Parameters * **data** (dict): Contains a dict that will be turned into a json object and sent to the API. ## Returns Dict containing the response from NetBox's API. ## Raises * RequestError if req.ok returns false. * AllocationError if req.status_code is 409 (Conflict) as with available-ips and available-prefixes when there is no room for the requested allocation. * ContentError if response is not json. """ return self._make_call(verb="post", data=data) def delete(self, data=None): """Makes DELETE request. Makes a DELETE request to NetBox's API. ## Parameters * **data** (list): Contains a dict that will be turned into a json object and sent to the API. ## Returns True if successful. ## Raises RequestError if req.ok doesn't return True. """ return self._make_call(verb="delete", data=data) def patch(self, data): """Makes PATCH request. Makes a PATCH request to NetBox's API. ## Parameters * **data** (dict): Contains a dict that will be turned into a json object and sent to the API. ## Returns Dict containing the response from NetBox's API. ## Raises * RequestError if req.ok returns false. * ContentError if response is not json. """ return self._make_call(verb="patch", data=data) def options(self): """Makes an OPTIONS request. Makes an OPTIONS request to NetBox's API. ## Returns Dict containing the response from NetBox's API. ## Raises * RequestError if req.ok returns false. * ContentError if response is not json. """ return self._make_call(verb="options") def get_count(self, *args, **kwargs): """Returns object count for query. Makes a query to the endpoint with ``limit=1`` set and only returns the value of the "count" field. ## Returns Int of number of objects query returned. ## Raises * RequestError if req.ok returns false. * ContentError if response is not json. """ if not hasattr(self, "count"): self.count = self._make_call(add_params={"limit": 1, "brief": 1})["count"] return self.count ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/core/response.py0000644000175100017510000005750715136437134020100 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import copy from urllib.parse import urlsplit import pynetbox.core.app from pynetbox.core.query import Request from pynetbox.core.util import Hashabledict # List of fields that are lists but should be treated as sets. LIST_AS_SET = ("tags", "tagged_vlans") def get_return(lookup, return_fields=None): """Returns simple representations for items passed to lookup. Used to return a "simple" representation of objects and collections sent to it via lookup. Otherwise, we look to see if lookup is a "choices" field (dict with only 'id' and 'value') or a nested_return. Finally, we check if it's a Record, if so simply return a string. Order is important due to nested_return being self-referential. ## Parameters * **return_fields** (list, optional): A list of fields to reference when calling values on lookup. """ for i in return_fields or ["id", "value", "nested_return"]: if isinstance(lookup, dict) and lookup.get(i): return lookup[i] else: if hasattr(lookup, i): # check if this is a "choices" field record # from a NetBox 2.7 server. if sorted(dict(lookup)) == sorted(["id", "value", "label"]): return getattr(lookup, "value") return getattr(lookup, i) if isinstance(lookup, Record): return str(lookup) else: return lookup def flatten_custom(custom_dict): ret = {} for k, val in custom_dict.items(): current_val = val if isinstance(val, dict): current_val = val.get("id", val) if isinstance(val, list): current_val = [v.get("id", v) if isinstance(v, dict) else v for v in val] ret[k] = current_val return ret class JsonField: """Explicit field type for values that are not to be converted to a Record object.""" _json_field = True class RecordSet: """Iterator containing Record objects. Returned by `Endpoint.all()` and `Endpoint.filter()` methods. Allows iteration of and actions to be taken on the results from the aforementioned methods. Contains Record objects. ## Examples To see how many results are in a query by calling `len()`: ```python x = nb.dcim.devices.all() len(x) # 123 ``` Simple iteration of the results: ```python devices = nb.dcim.devices.all() for device in devices: print(device.name) # test1-leaf1 # test1-leaf2 # test1-leaf3 ``` """ def __init__(self, endpoint, request, **kwargs): self.endpoint = endpoint self.request = request self.response = self.request.get() self._response_cache = [] def __iter__(self): return self def __next__(self): if self._response_cache: return self.endpoint.return_obj( self._response_cache.pop(), self.endpoint.api, self.endpoint ) return self.endpoint.return_obj( next(self.response), self.endpoint.api, self.endpoint ) def __len__(self): try: return self.request.count except AttributeError: try: self._response_cache.append(next(self.response)) except StopIteration: return 0 return self.request.count def update(self, **kwargs): """Updates kwargs onto all Records in the RecordSet and saves these. Updates are only sent to the API if a value were changed, and only for the Records which were changed. ## Returns True if the update succeeded, None if no update were required. ## Examples ```python result = nb.dcim.devices.filter(site_id=1).update(status='active') # True ``` """ updates = [] for record in self: # Update each record and determine if anything was updated for k, v in kwargs.items(): setattr(record, k, v) record_updates = record.updates() if record_updates: # if updated, add the id to the dict and append to list of updates record_updates["id"] = record.id updates.append(record_updates) if updates: return self.endpoint.update(updates) else: return None def delete(self): """Bulk deletes objects in a RecordSet. Allows for batch deletion of multiple objects in a RecordSet. ## Returns True if bulk DELETE operation was successful. ## Examples Deleting offline `devices` on site 1: ```python netbox.dcim.devices.filter(site_id=1, status="offline").delete() ``` """ return self.endpoint.delete(self) class Record: """Create Python objects from NetBox API responses. Creates an object from a NetBox response passed as `values`. Nested dicts that represent other endpoints are also turned into Record objects. All fields are then assigned to the object's attributes. If a missing attr is requested (e.g. requesting a field that's only present on a full response on a Record made from a nested response) then pynetbox will make a request for the full object and return the requested value. ## Examples Default representation of the object is usually its name: ```python x = nb.dcim.devices.get(1) x # test1-switch1 ``` Querying a string field: ```python x = nb.dcim.devices.get(1) x.serial # 'ABC123' ``` Querying a field on a nested object: ```python x = nb.dcim.devices.get(1) x.device_type.model # 'QFX5100-24Q' ``` Casting the object as a dictionary: ```python from pprint import pprint pprint(dict(x)) { 'asset_tag': None, 'cluster': None, 'comments': '', 'config_context': {}, 'created': '2018-04-01', 'custom_fields': {}, 'role': { 'id': 1, 'name': 'Test Switch', 'slug': 'test-switch', 'url': 'http://localhost:8000/api/dcim/device-roles/1/' }, 'device_type': {...}, 'display_name': 'test1-switch1', 'face': {'label': 'Rear', 'value': 1}, 'id': 1, 'name': 'test1-switch1', 'parent_device': None, 'platform': {...}, 'position': 1, 'primary_ip': { 'address': '192.0.2.1/24', 'family': 4, 'id': 1, 'url': 'http://localhost:8000/api/ipam/ip-addresses/1/' }, 'primary_ip4': {...}, 'primary_ip6': None, 'rack': { 'display_name': 'Test Rack', 'id': 1, 'name': 'Test Rack', 'url': 'http://localhost:8000/api/dcim/racks/1/' }, 'site': { 'id': 1, 'name': 'TEST', 'slug': 'TEST', 'url': 'http://localhost:8000/api/dcim/sites/1/' }, 'status': {'label': 'Active', 'value': 1}, 'tags': [], 'tenant': None, 'vc_position': None, 'vc_priority': None, 'virtual_chassis': None } ``` Iterating over a Record object: ```python for i in x: print(i) # ('id', 1) # ('name', 'test1-switch1') # ('display_name', 'test1-switch1') ``` """ url = None def __init__(self, values, api, endpoint): self.has_details = False self._full_cache = [] self._init_cache = [] self.api = api self.default_ret = Record self.endpoint = ( self._endpoint_from_url(values["url"]) if values and "url" in values and values["url"] else endpoint ) if values: self._parse_values(values) def __getattr__(self, k): """Default behavior for missing attrs. We'll call `full_details()` if we're asked for an attribute we don't have. In order to prevent non-explicit behavior,`k='keys'` is excluded because casting to dict() calls this attr. """ if self.url: if self.has_details is False and k != "keys": if self.full_details(): ret = getattr(self, k, None) if ret or hasattr(self, k): return ret raise AttributeError('object has no attribute "{}"'.format(k)) def __iter__(self): for i in dict(self._init_cache): cur_attr = getattr(self, i) if isinstance(cur_attr, Record): yield i, dict(cur_attr) elif isinstance(cur_attr, list) and all( isinstance(i, (Record, GenericListObject)) for i in cur_attr ): yield i, [dict(x) for x in cur_attr] else: yield i, cur_attr def __getitem__(self, k): return dict(self)[k] def __str__(self): return ( getattr(self, "name", None) or getattr(self, "label", None) or getattr(self, "display", None) or "" ) def __repr__(self): return str(self) def __getstate__(self): return self.__dict__ def __setstate__(self, d): self.__dict__.update(d) def __key__(self): if hasattr(self, "id"): return (self.endpoint.name, self.id) else: return self.endpoint.name def __hash__(self): return hash(self.__key__()) def __eq__(self, other): if isinstance(other, Record): return self.__key__() == other.__key__() return NotImplemented def _extract_app_endpoint(self, url): """Extract app/endpoint from a NetBox API URL. Extracts the app and endpoint portion from a URL like: https://netbox/api/dcim/rear-ports/12761/ Returns: String like "dcim/rear-ports" """ app_endpoint = "/".join( urlsplit(url).path[len(urlsplit(self.api.base_url).path) :].split("/")[1:3] ) return app_endpoint def _get_obj_class(self, url): """Map API URL to corresponding Record class for cable tracing. Used by TraceableRecord and PathableRecord to deserialize objects encountered in cable trace/path responses. """ # Import here to avoid circular dependency from pynetbox.models.circuits import CircuitTerminations from pynetbox.models.dcim import ( Cables, ConsolePorts, ConsoleServerPorts, FrontPorts, Interfaces, PowerFeeds, PowerOutlets, PowerPorts, RearPorts, ) uri_to_obj_class_map = { "circuits/circuit-terminations": CircuitTerminations, "dcim/cables": Cables, "dcim/console-ports": ConsolePorts, "dcim/console-server-ports": ConsoleServerPorts, "dcim/front-ports": FrontPorts, "dcim/interfaces": Interfaces, "dcim/power-feeds": PowerFeeds, "dcim/power-outlets": PowerOutlets, "dcim/power-ports": PowerPorts, "dcim/rear-ports": RearPorts, } app_endpoint = self._extract_app_endpoint(url) return uri_to_obj_class_map.get(app_endpoint, Record) def _add_cache(self, item): key, value = item self._init_cache.append((key, get_return(value))) def _parse_values(self, values): """Parses values init arg. Parses values dict at init and sets object attributes with the values within. """ def generic_list_parser(key_name, list_item): from pynetbox.models.mapper import CONTENT_TYPE_MAPPER if ( isinstance(list_item, dict) and "object_type" in list_item and "object" in list_item ): lookup = list_item["object_type"] if model := CONTENT_TYPE_MAPPER.get(lookup, None): record = model(list_item["object"], self.api, self.endpoint) return GenericListObject(record) return list_item def list_parser(key_name, list_item): if isinstance(list_item, dict): lookup = getattr(self.__class__, key_name, None) if not isinstance(lookup, list): # This is *list_parser*, so if the custom model field is not # a list (or is not defined), just return the default model return self.default_ret(list_item, self.api, self.endpoint) else: model = lookup[0] return model(list_item, self.api, self.endpoint) return list_item for k, v in values.items(): if isinstance(v, dict): lookup = getattr(self.__class__, k, None) if k in ["custom_fields", "local_context_data"] or hasattr( lookup, "_json_field" ): self._add_cache((k, copy.deepcopy(v))) setattr(self, k, v) continue if lookup: v = lookup(v, self.api, self.endpoint) else: v = self.default_ret(v, self.api, self.endpoint) self._add_cache((k, v)) elif isinstance(v, list): # check if GFK if len(v) and isinstance(v[0], dict) and "object_type" in v[0]: v = [generic_list_parser(k, i) for i in v] to_cache = [i.serialize() for i in v] elif k == "constraints": # Permissions constraints can be either dict or list to_cache = copy.deepcopy(v) else: v = [list_parser(k, i) for i in v] to_cache = list(v) self._add_cache((k, to_cache)) else: self._add_cache((k, v)) setattr(self, k, v) def _endpoint_from_url(self, url): url_path = urlsplit(url).path base_url_path_parts = urlsplit(self.api.base_url).path.split("/") if len(base_url_path_parts) > 2: # There are some extra directories in the path, remove them from url extra_path = "/".join(base_url_path_parts[:-1]) url_path = url_path[len(extra_path) :] split_url_path = url_path.split("/") if split_url_path[2] == "plugins": app = "plugins/{}".format(split_url_path[3]) name = split_url_path[4] else: app, name = split_url_path[2:4] return getattr(pynetbox.core.app.App(self.api, app), name) def full_details(self): """Queries the hyperlinked endpoint if 'url' is defined. This method will populate the attributes from the detail endpoint when it's called. Sets the class-level `has_details` attribute when it's called to prevent being called more than once. :returns: True """ if self.url: req = Request( base=self.url, token=self.api.token, http_session=self.api.http_session, ) self._parse_values(next(req.get())) self.has_details = True return True return False def serialize(self, nested=False, init=False): """Serializes an object Pulls all the attributes in an object and creates a dict that can be turned into the json that netbox is expecting. If an attribute's value is a ``Record`` type it's replaced with the ``id`` field of that object. .. note:: Using this to get a dictionary representation of the record is discouraged. It's probably better to cast to dict() instead. See Record docstring for example. :returns: dict. """ if nested: return get_return(self) if init: init_vals = dict(self._init_cache) ret = {} for i in dict(self): current_val = getattr(self, i) if not init else init_vals.get(i) if i == "custom_fields": ret[i] = flatten_custom(current_val) else: if isinstance(current_val, Record): current_val = getattr(current_val, "serialize")(nested=True) if isinstance(current_val, list): serialized_list = [] for v in current_val: if isinstance(v, GenericListObject): v = v.serialize() elif isinstance(v, Record): v = v.id serialized_list.append(v) current_val = serialized_list if i in LIST_AS_SET and ( all([isinstance(v, str) for v in current_val]) or all([isinstance(v, int) for v in current_val]) ): current_val = list(dict.fromkeys(current_val)) ret[i] = current_val return ret def _diff(self): def fmt_dict(k, v): if isinstance(v, dict): return k, Hashabledict(v) if isinstance(v, list): return k, ",".join(map(str, v)) return k, v current = Hashabledict({fmt_dict(k, v) for k, v in self.serialize().items()}) init = Hashabledict( {fmt_dict(k, v) for k, v in self.serialize(init=True).items()} ) return set([i[0] for i in set(current.items()) ^ set(init.items())]) def updates(self): """Compiles changes for an existing object into a dict. Takes a diff between the objects current state and its state at init and returns them as a dictionary, which will be empty if no changes. :returns: dict. :example: >>> x = nb.dcim.devices.get(name='test1-a3-tor1b') >>> x.serial '' >>> x.serial = '1234' >>> x.updates() {'serial': '1234'} >>> """ if self.id: diff = self._diff() if diff: serialized = self.serialize() return {i: serialized[i] for i in diff} return {} def save(self): """Saves changes to an existing object. Takes a diff between the objects current state and its state at init and sends them as a dictionary to Request.patch(). :returns: True if PATCH request was successful. :example: >>> x = nb.dcim.devices.get(name='test1-a3-tor1b') >>> x.serial '' >>> x.serial = '1234' >>> x.save() True >>> """ updates = self.updates() if updates: req = Request( key=self.id, base=self.endpoint.url, token=self.api.token, http_session=self.api.http_session, ) result = req.patch(updates) if result: # Update object state with response from PATCH to keep cache in sync self._parse_values(result) return True return False def update(self, data): """Update an object with a dictionary. Accepts a dict and uses it to update the record and call save(). For nested and choice fields you'd pass an int the same as if you were modifying the attribute and calling save(). :arg dict data: Dictionary containing the k/v to update the record object with. :returns: True if PATCH request was successful. :example: >>> x = nb.dcim.devices.get(1) >>> x.update({ ... "name": "test-switch2", ... "serial": "ABC321", ... }) True """ for k, v in data.items(): setattr(self, k, v) return self.save() def delete(self): """Deletes an existing object. :returns: True if DELETE operation was successful. :example: >>> x = nb.dcim.devices.get(name='test1-a3-tor1b') >>> x.delete() True >>> """ req = Request( key=self.id, base=self.endpoint.url, token=self.api.token, http_session=self.api.http_session, ) return True if req.delete() else False class PathableRecord(Record): """Record class for objects that support cable path tracing via /paths endpoint. Front ports, rear ports, and circuit terminations use the /paths endpoint to show complete cable paths from origin to destination. """ def _build_endpoint_object(self, endpoint_data): if not endpoint_data: return None return_obj_class = self._get_obj_class(endpoint_data["url"]) return return_obj_class(endpoint_data, self.endpoint.api, self.endpoint) def paths(self): """Return all cable paths traversing this pass-through port. Returns a list of dictionaries, each containing: - origin: The starting endpoint of the path (or None if not connected) - destination: The ending endpoint of the path (or None if not connected) - path: List of path segments, where each segment is a list of Record objects (similar to the trace() endpoint structure) """ req = Request( key=str(self.id) + "/paths", base=self.endpoint.url, token=self.api.token, http_session=self.api.http_session, ).get() ret = [] for path_data in req: path_segments = [] for segment_data in path_data.get("path", []): segment_objects = [] if isinstance(segment_data, list): for item_data in segment_data: segment_obj = self._build_endpoint_object(item_data) if segment_obj: segment_objects.append(segment_obj) else: segment_obj = self._build_endpoint_object(segment_data) if segment_obj: segment_objects.append(segment_obj) path_segments.append(segment_objects) origin = self._build_endpoint_object(path_data.get("origin")) destination = self._build_endpoint_object(path_data.get("destination")) ret.append({ "origin": origin, "destination": destination, "path": path_segments, }) return ret class GenericListObject: def __init__(self, record): from pynetbox.models.mapper import TYPE_CONTENT_MAPPER self.object = record self.object_id = record.id self.object_type = TYPE_CONTENT_MAPPER.get(record.__class__) def __repr__(self): return str(self.object) def serialize(self): ret = {k: getattr(self, k) for k in ["object_id", "object_type"]} return ret def __getattr__(self, k): return getattr(self.object, k) def __iter__(self): for i in ["object_id", "object_type", "object"]: cur_attr = getattr(self, i) if isinstance(cur_attr, Record): cur_attr = dict(cur_attr) yield i, cur_attr ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/core/util.py0000644000175100017510000000012715136437134017201 0ustar00runnerrunnerclass Hashabledict(dict): def __hash__(self): return hash(frozenset(self)) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8013303 pynetbox-7.6.1/pynetbox/models/0000755000175100017510000000000015136437144016206 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/models/__init__.py0000644000175100017510000000000015136437134020304 0ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/models/circuits.py0000644000175100017510000000307315136437134020407 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from pynetbox.core.response import PathableRecord, Record class Circuits(Record): def __str__(self): return self.cid class CircuitTerminations(PathableRecord): """Circuit Termination Record with cable path tracing support. Circuit terminations support cable path tracing via the paths() method, which returns all cable paths traversing this circuit termination point. """ def __str__(self): return self.circuit.cid class VirtualCircuits(Record): """Virtual Circuit Record. Virtual circuits represent L2VPN-like connections delivered across one or more physical circuits. """ def __str__(self): return self.cid class VirtualCircuitTerminations(PathableRecord): """Virtual Circuit Termination Record with cable path tracing support. Virtual circuit terminations support cable path tracing via the paths() method, which returns all cable paths traversing this termination point. """ def __str__(self): return self.virtual_circuit.cid ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/models/core.py0000644000175100017510000000152515136437134017512 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from pynetbox.core.response import JsonField, Record class DataSources(Record): pass class Jobs(Record): pass class ObjectChanges(Record): object_data = JsonField postchange_data = JsonField prechange_data = JsonField def __str__(self): return self.request_id ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/models/dcim.py0000644000175100017510000001466015136437134017502 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from pynetbox.core.endpoint import ( DetailEndpoint, RODetailEndpoint, ROMultiFormatDetailEndpoint, ) from pynetbox.core.query import Request from pynetbox.core.response import JsonField, PathableRecord, Record from pynetbox.models.circuits import Circuits from pynetbox.models.ipam import IpAddresses class TraceableRecord(Record): def _build_termination_data(self, termination_list): terminations_data = [] for hop_item_data in termination_list: return_obj_class = self._get_obj_class(hop_item_data["url"]) terminations_data.append( return_obj_class(hop_item_data, self.endpoint.api, self.endpoint) ) return terminations_data def trace(self): req = Request( key=str(self.id) + "/trace", base=self.endpoint.url, token=self.api.token, http_session=self.api.http_session, ).get() ret = [] for a_terminations_data, cable_data, b_terminations_data in req: ret.append(self._build_termination_data(a_terminations_data)) if not cable_data: ret.append(cable_data) else: return_obj_class = self._get_obj_class(cable_data["url"]) ret.append( return_obj_class(cable_data, self.endpoint.api, self.endpoint) ) ret.append(self._build_termination_data(b_terminations_data)) return ret class DeviceTypes(Record): def __str__(self): return self.model class Devices(Record): """Devices Object. Represents a device response from netbox. ## Attributes * **primary_ip, ip4, ip6** (list): Tells __init__ in Record() to take the `primary_ip` field's value from the API response and return an initialized list of IpAddress objects * **device_type** (obj): Tells __init__ in Record() to take the `device_type` field's value from the API response and return an initialized DeviceType object """ has_details = True device_type = DeviceTypes primary_ip = IpAddresses primary_ip4 = IpAddresses primary_ip6 = IpAddresses local_context_data = JsonField config_context = JsonField @property def napalm(self): """Represents the ``napalm`` detail endpoint. Returns a DetailEndpoint object that is the interface for viewing response from the napalm endpoint. ## Returns DetailEndpoint object. ## Examples ```python device = nb.ipam.devices.get(123) device.napalm.list(method='get_facts') # {"get_facts": {"interface_list": ["ge-0/0/0"]}} ``` """ return RODetailEndpoint(self, "napalm") @property def render_config(self): """Represents the ``render-config`` detail endpoint. Returns a DetailEndpoint object that is the interface for viewing response from the render-config endpoint. ## Returns DetailEndpoint object. ## Examples ```python device = nb.ipam.devices.get(123) device.render_config.create() ``` """ return DetailEndpoint(self, "render-config") class InterfaceConnections(Record): def __str__(self): return self.interface_a.name class InterfaceConnection(Record): def __str__(self): return self.interface.name class Interfaces(TraceableRecord): interface_connection = InterfaceConnection class PowerFeeds(TraceableRecord): pass class PowerOutlets(TraceableRecord): device = Devices class PowerPorts(TraceableRecord): device = Devices class ConsolePorts(TraceableRecord): device = Devices class ConsoleServerPorts(TraceableRecord): device = Devices class RackReservations(Record): def __str__(self): return self.description class VirtualChassis(Record): master = Devices class RUs(Record): device = Devices class FrontPorts(PathableRecord): device = Devices class RearPorts(PathableRecord): device = Devices class Racks(Record): @property def units(self): """Represents the ``units`` detail endpoint. Returns a DetailEndpoint object that is the interface for viewing response from the units endpoint. ## Returns DetailEndpoint object. ## Examples ```python rack = nb.dcim.racks.get(123) rack.units.list() # {"get_facts": {"interface_list": ["ge-0/0/0"]}} ``` """ return RODetailEndpoint(self, "units", custom_return=RUs) @property def elevation(self): """Represents the ``elevation`` detail endpoint. Returns a multi-format endpoint supporting both JSON and SVG responses. The elevation endpoint provides rack unit information and can render graphical elevation views. ## Returns ROMultiFormatDetailEndpoint object supporting JSON and SVG formats. ## Examples ```python rack = nb.dcim.racks.get(123) # Get rack units as JSON (list of RU objects) rack.elevation.list() # Get elevation as SVG diagram svg = rack.elevation.list(render='svg') ``` """ return ROMultiFormatDetailEndpoint(self, "elevation", custom_return=RUs) class Termination(Record): def __str__(self): # hacky check to see if we're a circuit termination to # avoid another call to NetBox because of a non-existent attr # in self.name if "circuit" in str(self.url): return self.circuit.cid return self.name device = Devices circuit = Circuits class Cables(Record): def __str__(self): if len(self.a_terminations) == 1 and len(self.b_terminations) == 1: return "{} <> {}".format(self.a_terminations[0], self.b_terminations[0]) return "Cable #{}".format(self.id) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/models/extras.py0000644000175100017510000000122415136437134020064 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from pynetbox.core.response import JsonField, Record class ConfigContexts(Record): data = JsonField ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/models/ipam.py0000644000175100017510000001302615136437134017507 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from pynetbox.core.endpoint import DetailEndpoint from pynetbox.core.response import Record class IpAddresses(Record): def __str__(self): return str(self.address) class IpRanges(Record): def __str__(self): return str(self.display) @property def available_ips(self): """Represents the ``available-ips`` detail endpoint. Returns a DetailEndpoint object that is the interface for viewing and creating IP addresses inside an ip range. ## Returns DetailEndpoint object. ## Examples ```python ip_range = nb.ipam.ip_ranges.get(24) ip_range.available_ips.list() # [10.0.0.1/24, 10.0.0.2/24, 10.0.0.3/24, 10.0.0.4/24, 10.0.0.5/24, ...] ``` To create a single IP: ```python ip_range = nb.ipam.ip_ranges.get(24) ip_range.available_ips.create() # 10.0.0.1/24 ``` To create multiple IPs: ```python ip_range = nb.ipam.ip_ranges.get(24) create = ip_range.available_ips.create([{} for i in range(2)]) # [10.0.0.2/24, 10.0.0.3/24] ``` """ return DetailEndpoint(self, "available-ips", custom_return=IpAddresses) class Prefixes(Record): def __str__(self): return str(self.prefix) @property def available_ips(self): """Represents the ``available-ips`` detail endpoint. Returns a DetailEndpoint object that is the interface for viewing and creating IP addresses inside a prefix. ## Returns DetailEndpoint object. ## Examples ```python prefix = nb.ipam.prefixes.get(24) prefix.available_ips.list() # [10.0.0.1/24, 10.0.0.2/24, 10.0.0.3/24, 10.0.0.4/24, 10.0.0.5/24, ...] ``` To create a single IP: ```python prefix = nb.ipam.prefixes.get(24) prefix.available_ips.create() # 10.0.0.1/24 ``` To create multiple IPs: ```python prefix = nb.ipam.prefixes.get(24) create = prefix.available_ips.create([{} for i in range(2)]) # [10.0.0.2/24, 10.0.0.3/24] ``` """ return DetailEndpoint(self, "available-ips", custom_return=IpAddresses) @property def available_prefixes(self): """Represents the ``available-prefixes`` detail endpoint. Returns a DetailEndpoint object that is the interface for viewing and creating prefixes inside a parent prefix. Very similar to `available_ips`, except that dict (or list of dicts) passed to `.create()` needs to have a `prefix_length` key/value specified. ## Returns DetailEndpoint object. ## Examples ```python prefix = nb.ipam.prefixes.get(3) prefix # 10.0.0.0/16 prefix.available_prefixes.list() # [10.0.1.0/24, 10.0.2.0/23, 10.0.4.0/22, 10.0.8.0/21, 10.0.16.0/20, 10.0.32.0/19, 10.0.64.0/18, 10.0.128.0/17] ``` Creating a single child prefix: ```python prefix = nb.ipam.prefixes.get(1) prefix # 10.0.0.0/24 new_prefix = prefix.available_prefixes.create( {"prefix_length": 29} ) # 10.0.0.16/29 ``` """ return DetailEndpoint(self, "available-prefixes", custom_return=Prefixes) class Aggregates(Record): def __str__(self): return str(self.prefix) class Vlans(Record): def __str__(self): return super().__str__() or str(self.vid) class VlanGroups(Record): @property def available_vlans(self): """Represents the ``available-vlans`` detail endpoint. Returns a DetailEndpoint object that is the interface for viewing and creating VLANs inside a VLAN group. ## Returns DetailEndpoint object. ## Examples ```python vlan_group = nb.ipam.vlan_groups.get(1) vlan_group.available_vlans.list() # [10, 11, 12] ``` To create a new VLAN: ```python vlan_group.available_vlans.create({"name": "NewVLAN"}) # NewVLAN (10) ``` """ return DetailEndpoint(self, "available-vlans", custom_return=Vlans) class AsnRanges(Record): @property def available_asns(self): """Represents the ``available-asns`` detail endpoint. Returns a DetailEndpoint object that is the interface for viewing and creating ASNs inside an ASN range. ## Returns DetailEndpoint object. ## Examples ```python asn_range = nb.ipam.asn_ranges.get(1) asn_range.available_asns.list() # [64512, 64513, 64514] ``` To create a new ASN: ```python asn_range.available_asns.create() # 64512 ``` To create multiple ASNs: ```python asn_range.available_asns.create([{} for i in range(2)]) # [64513, 64514] ``` """ return DetailEndpoint(self, "available-asns") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/models/mapper.py0000644000175100017510000000731415136437134020050 0ustar00runnerrunnerfrom .circuits import ( Circuits, CircuitTerminations, VirtualCircuits, VirtualCircuitTerminations, ) from .core import DataSources, Jobs, ObjectChanges from .dcim import ( Cables, ConsolePorts, ConsoleServerPorts, Devices, DeviceTypes, FrontPorts, Interfaces, PowerFeeds, PowerOutlets, PowerPorts, RackReservations, Racks, RearPorts, Termination, VirtualChassis, ) from .ipam import Aggregates, IpAddresses, Prefixes, VlanGroups, Vlans from .virtualization import VirtualMachines from .wireless import WirelessLans CONTENT_TYPE_MAPPER = { "circuits.circuit": Circuits, "circuits.circuittermination": CircuitTerminations, "circuits.virtualcircuit": VirtualCircuits, "circuits.virtualcircuittermination": VirtualCircuitTerminations, "core.datasource": DataSources, "core.job": Jobs, "core.objectchange": ObjectChanges, "dcim.cable": Cables, "dcim.cablepath": None, "dcim.cabletermination": Termination, "dcim.consoleport": ConsolePorts, "dcim.consoleporttemplate": None, "dcim.consoleserverport": ConsoleServerPorts, "dcim.consoleserverporttemplate": None, "dcim.device": Devices, "dcim.devicebay": None, "dcim.devicebaytemplate": None, "dcim.devicerole": None, "dcim.devicetype": DeviceTypes, "dcim.frontport": FrontPorts, "dcim.frontporttemplate": None, "dcim.interface": Interfaces, "dcim.interfacetemplate": None, "dcim.inventoryitem": None, "dcim.inventoryitemrole": None, "dcim.inventoryitemtemplate": None, "dcim.location": None, "dcim.manufacturer": None, "dcim.module": None, "dcim.modulebay": None, "dcim.modulebaytemplate": None, "dcim.moduletype": None, "dcim.platform": None, "dcim.powerfeed": PowerFeeds, "dcim.poweroutlet": PowerOutlets, "dcim.poweroutlettemplate": None, "dcim.powerpanel": None, "dcim.powerport": PowerPorts, "dcim.powerporttemplate": None, "dcim.rack": Racks, "dcim.rackreservation": RackReservations, "dcim.rackrole": None, "dcim.rearport": RearPorts, "dcim.rearporttemplate": None, "dcim.region": None, "dcim.site": None, "dcim.sitegroup": None, "dcim.virtualchassis": VirtualChassis, "extras.configcontext": None, "extras.configrevision": None, "extras.customfield": None, "extras.customlink": None, "extras.exporttemplate": None, "extras.imageattachment": None, "extras.jobresult": None, "extras.journalentry": None, "extras.report": None, "extras.script": None, "extras.tag": None, "extras.taggeditem": None, "extras.webhook": None, "ipam.aggregate": Aggregates, "ipam.ASN": None, "ipam.FHRPgroup": None, "ipam.FHRPgroupassignment": None, "ipam.IPaddress": IpAddresses, "ipam.IPrange": None, "ipam.L2VPN": None, "ipam.L2VPNtermination": None, "ipam.prefix": Prefixes, "ipam.RIR": None, "ipam.role": None, "ipam.routetarget": None, "ipam.service": None, "ipam.servicetemplate": None, "ipam.VLAN": Vlans, "ipam.VLANgroup": VlanGroups, "ipam.VRF": None, "tenancy.contact": None, "tenancy.contactassignment": None, "tenancy.contactgroup": None, "tenancy.contactrole": None, "tenancy.tenant": None, "tenancy.tenantgroup": None, "virtualization.cluster": None, "virtualization.clustergroup": None, "virtualization.clustertype": None, "virtualization.interface": None, "virtualization.virtualmachine": VirtualMachines, "wireless.WirelessLAN": WirelessLans, "wireless.WirelessLANGroup": None, "wireless.wirelesslink": None, } TYPE_CONTENT_MAPPER = {v: k for k, v in CONTENT_TYPE_MAPPER.items() if v is not None} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/models/users.py0000644000175100017510000000136715136437134017727 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from pynetbox.core.response import JsonField, Record class Users(Record): def __str__(self): return self.username class Permissions(Record): users = [Users] constraints = JsonField ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/models/virtualization.py0000644000175100017510000000245615136437134021652 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from pynetbox.core.endpoint import DetailEndpoint from pynetbox.core.response import JsonField, Record from pynetbox.models.ipam import IpAddresses class VirtualMachines(Record): primary_ip = IpAddresses primary_ip4 = IpAddresses primary_ip6 = IpAddresses config_context = JsonField @property def render_config(self): """ Represents the ``render-config`` detail endpoint. Returns a DetailEndpoint object that is the interface for viewing response from the render-config endpoint. :returns: :py:class:`.DetailEndpoint` :Examples: >>> vm = nb.virtualization.virtual_machines.get(123) >>> vm.render_config.create() """ return DetailEndpoint(self, "render-config") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/pynetbox/models/wireless.py0000644000175100017510000000124215136437134020413 0ustar00runnerrunner""" (c) 2017 DigitalOcean Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from pynetbox.core.response import Record class WirelessLans(Record): def __str__(self): return self.ssid ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8223305 pynetbox-7.6.1/pynetbox.egg-info/0000755000175100017510000000000015136437144016415 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619043.0 pynetbox-7.6.1/pynetbox.egg-info/PKG-INFO0000644000175100017510000001214615136437143017515 0ustar00runnerrunnerMetadata-Version: 2.4 Name: pynetbox Version: 7.6.1 Summary: NetBox API client library Home-page: https://github.com/netbox-community/pynetbox Author: Zach Moody, Arthur Hanson Author-email: ahanson@netboxlabs.com License: Apache2 Keywords: netbox Classifier: Intended Audience :: Developers Classifier: Development Status :: 5 - Production/Stable Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Description-Content-Type: text/markdown License-File: LICENSE Requires-Dist: requests<3.0,>=2.20.0 Requires-Dist: packaging Dynamic: author Dynamic: author-email Dynamic: classifier Dynamic: description Dynamic: description-content-type Dynamic: home-page Dynamic: keywords Dynamic: license Dynamic: license-file Dynamic: requires-dist Dynamic: summary # Pynetbox Python API client library for [NetBox](https://github.com/netbox-community/netbox). > **Note:** Version 6.7 and later of the library only supports NetBox 3.3 and above. ## Compatibility Each pyNetBox Version listed below has been tested with its corresponding NetBox Version. | NetBox Version | Plugin Version | |:--------------:|:--------------:| | 4.5 | 7.6.1 | | 4.5 | 7.6.0 | | 4.4 | 7.5.0 | | 4.3 | 7.5.0 | | 4.2 | 7.5.0 | | 4.1 | 7.5.0 | | 4.0.6 | 7.4.1 | | 4.0.0 | 7.3.4 | | 3.7 | 7.3.0 | | 3.6 | 7.2.0 | | 3.5 | 7.1.0 | | 3.3 | 7.0.0 | ## Installation To install run `pip install pynetbox`. Alternatively, you can clone the repo and run `python setup.py install`. ## Quick Start The full pynetbox API is documented on [GitHub Pages](https://netbox-community.github.io/pynetbox/), but the following should be enough to get started using it. To begin, import pynetbox and instantiate the API. ``` import pynetbox nb = pynetbox.api( 'http://localhost:8000', token='d6f4e314a5b5fefd164995169f28ae32d987704f' ) ``` The first argument the .api() method takes is the NetBox URL. There are a handful of named arguments you can provide, but in most cases none are required to simply pull data. In order to write, the `token` argument should to be provided. ## Queries The pynetbox API is setup so that NetBox's apps are attributes of the `.api()` object, and in turn those apps have attribute representing each endpoint. Each endpoint has a handful of methods available to carry out actions on the endpoint. For example, in order to query all the objects in the `devices` endpoint you would do the following: ``` >>> devices = nb.dcim.devices.all() >>> for device in devices: ... print(device.name) ... test1-leaf1 test1-leaf2 test1-leaf3 >>> ``` Note that the all() and filter() methods are generators and return an object that can be iterated over only once. If you are going to be iterating over it repeatedly you need to either call the all() method again, or encapsulate the results in a `list` object like this: ``` >>> devices = list(nb.dcim.devices.all()) ``` ### Threading pynetbox supports multithreaded calls for `.filter()` and `.all()` queries. It is **highly recommended** you have `MAX_PAGE_SIZE` in your Netbox install set to anything *except* `0` or `None`. The default value of `1000` is usually a good value to use. To enable threading, add `threading=True` parameter to the `.api`: ```python nb = pynetbox.api( 'http://localhost:8000', threading=True, ) ``` ### Filters validation NetBox doesn't validate filters passed to the GET API endpoints, which are accessed with `.get()` and `.filter()`. If a filter is incorrect, NetBox silently returns the entire database table content. Pynetbox allows to check provided parameters against NetBox OpenAPI specification before doing the call, and raise an exception if a parameter is incorrect. This can be enabled globally by setting `strict_filters=True` in the API object initialization: ```python nb = pynetbox.api( 'http://localhost:8000', strict_filters=True, ) ``` This can also be enabled and disabled on a per-request basis: ```python # Disable for one request when enabled globally. # Will not raise an exception and return the entire Device table. nb.dcim.devices.filter(non_existing_filter="aaaa", strict_filters=False) # Enable for one request when not enabled globally. # Will raise an exception. nb.dcim.devices.filter(non_existing_filter="aaaa", strict_filters=True) ``` ## Running Tests First, create and activate a Python virtual environment in the pynetbox directory to isolate the project dependencies: ```python python3 -m venv venv source venv/bin/activate ``` Install both requirements files: ```python pip install -r requirements.txt pip install -r requirements-dev.txt ``` The test suite requires Docker to be installed and running, as it will download and launch netbox-docker containers during test execution. With Docker installed and running, execute the following command to run the test suite: ```python pytest ``` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619043.0 pynetbox-7.6.1/pynetbox.egg-info/SOURCES.txt0000644000175100017510000001470415136437143020306 0ustar00runnerrunner.gitignore .pre-commit-config.yaml .readthedocs.yaml CHANGELOG.md LICENSE README.md mkdocs.yml requirements-dev.txt requirements.txt setup.py .github/PULL_REQUEST_TEMPLATE.md .github/ISSUE_TEMPLATE/bug_report.yaml .github/ISSUE_TEMPLATE/config.yml .github/ISSUE_TEMPLATE/deprecation.yaml .github/ISSUE_TEMPLATE/documentation_change.yaml .github/ISSUE_TEMPLATE/feature_request.yaml .github/ISSUE_TEMPLATE/housekeeping.yaml .github/workflows/build-mkdocs.yml .github/workflows/publish.yml .github/workflows/py3.yml docs/IPAM.md docs/advanced.md docs/api.md docs/branching.md docs/circuits.md docs/dcim.md docs/endpoint.md docs/getting-started.md docs/index.md docs/installation.md docs/release-notes.md docs/request.md docs/response.md docs/virtualization.md docs/development/getting-started.md docs/development/index.md docs/development/release-checklist.md docs/stylesheets/extra.css pynetbox/__init__.py pynetbox.egg-info/PKG-INFO pynetbox.egg-info/SOURCES.txt pynetbox.egg-info/dependency_links.txt pynetbox.egg-info/not-zip-safe pynetbox.egg-info/requires.txt pynetbox.egg-info/top_level.txt pynetbox/core/__init__.py pynetbox/core/api.py pynetbox/core/app.py pynetbox/core/endpoint.py pynetbox/core/query.py pynetbox/core/response.py pynetbox/core/util.py pynetbox/models/__init__.py pynetbox/models/circuits.py pynetbox/models/core.py pynetbox/models/dcim.py pynetbox/models/extras.py pynetbox/models/ipam.py pynetbox/models/mapper.py pynetbox/models/users.py pynetbox/models/virtualization.py pynetbox/models/wireless.py tests/__init__.py tests/conftest.py tests/test_api.py tests/test_app.py tests/test_circuits.py tests/test_tenancy.py tests/test_users.py tests/test_virtualization.py tests/test_wireless.py tests/util.py tests/fixtures/api/token_provision.json tests/fixtures/circuits/circuit.json tests/fixtures/circuits/circuit_termination.json tests/fixtures/circuits/circuit_terminations.json tests/fixtures/circuits/circuit_type.json tests/fixtures/circuits/circuit_types.json tests/fixtures/circuits/circuits.json tests/fixtures/circuits/provider.json tests/fixtures/circuits/providers.json tests/fixtures/dcim/cable.json tests/fixtures/dcim/cables.json tests/fixtures/dcim/choices.json tests/fixtures/dcim/console_port.json tests/fixtures/dcim/console_port_template.json tests/fixtures/dcim/console_port_templates.json tests/fixtures/dcim/console_ports.json tests/fixtures/dcim/console_server_port.json tests/fixtures/dcim/console_server_port_template.json tests/fixtures/dcim/console_server_port_templates.json tests/fixtures/dcim/console_server_ports.json tests/fixtures/dcim/device.json tests/fixtures/dcim/device_bay.json tests/fixtures/dcim/device_bay_template.json tests/fixtures/dcim/device_bay_templates.json tests/fixtures/dcim/device_bays.json tests/fixtures/dcim/device_bulk_create.json tests/fixtures/dcim/device_role.json tests/fixtures/dcim/device_roles.json tests/fixtures/dcim/device_type.json tests/fixtures/dcim/device_types.json tests/fixtures/dcim/devices.json tests/fixtures/dcim/interface.json tests/fixtures/dcim/interface_connection.json tests/fixtures/dcim/interface_connections.json tests/fixtures/dcim/interface_template.json tests/fixtures/dcim/interface_templates.json tests/fixtures/dcim/interface_trace.json tests/fixtures/dcim/interfaces.json tests/fixtures/dcim/interfaces_1.json tests/fixtures/dcim/interfaces_2.json tests/fixtures/dcim/inventory_item.json tests/fixtures/dcim/inventory_items.json tests/fixtures/dcim/manufacturer.json tests/fixtures/dcim/manufacturers.json tests/fixtures/dcim/napalm.json tests/fixtures/dcim/platform.json tests/fixtures/dcim/platforms.json tests/fixtures/dcim/power_outlet.json tests/fixtures/dcim/power_outlet_template.json tests/fixtures/dcim/power_outlet_templates.json tests/fixtures/dcim/power_outlets.json tests/fixtures/dcim/power_port.json tests/fixtures/dcim/power_port_template.json tests/fixtures/dcim/power_port_templates.json tests/fixtures/dcim/power_ports.json tests/fixtures/dcim/rack.json tests/fixtures/dcim/rack_group.json tests/fixtures/dcim/rack_groups.json tests/fixtures/dcim/rack_reservation.json tests/fixtures/dcim/rack_reservations.json tests/fixtures/dcim/rack_role.json tests/fixtures/dcim/rack_roles.json tests/fixtures/dcim/rack_u.json tests/fixtures/dcim/racks.json tests/fixtures/dcim/region.json tests/fixtures/dcim/regions.json tests/fixtures/dcim/site.json tests/fixtures/dcim/sites.json tests/fixtures/dcim/virtual_chassis_device.json tests/fixtures/dcim/virtual_chassis_devices.json tests/fixtures/ipam/aggregate.json tests/fixtures/ipam/aggregates.json tests/fixtures/ipam/available-ips-post.json tests/fixtures/ipam/available-ips.json tests/fixtures/ipam/available-prefixes-post.json tests/fixtures/ipam/available-prefixes.json tests/fixtures/ipam/ip_address.json tests/fixtures/ipam/ip_addresses.json tests/fixtures/ipam/prefix.json tests/fixtures/ipam/prefixes.json tests/fixtures/ipam/rir.json tests/fixtures/ipam/rirs.json tests/fixtures/ipam/role.json tests/fixtures/ipam/roles.json tests/fixtures/ipam/vlan.json tests/fixtures/ipam/vlan_group.json tests/fixtures/ipam/vlan_groups.json tests/fixtures/ipam/vlans.json tests/fixtures/ipam/vrf.json tests/fixtures/ipam/vrfs.json tests/fixtures/tenancy/tenant.json tests/fixtures/tenancy/tenant_group.json tests/fixtures/tenancy/tenant_groups.json tests/fixtures/tenancy/tenants.json tests/fixtures/users/group.json tests/fixtures/users/groups.json tests/fixtures/users/permission.json tests/fixtures/users/permissions.json tests/fixtures/users/unknown_model.json tests/fixtures/users/user.json tests/fixtures/users/users.json tests/fixtures/virtualization/cluster.json tests/fixtures/virtualization/cluster_group.json tests/fixtures/virtualization/cluster_groups.json tests/fixtures/virtualization/cluster_type.json tests/fixtures/virtualization/cluster_types.json tests/fixtures/virtualization/clusters.json tests/fixtures/virtualization/interface.json tests/fixtures/virtualization/interfaces.json tests/fixtures/virtualization/virtual_machine.json tests/fixtures/virtualization/virtual_machines.json tests/fixtures/wireless/wireless_lan.json tests/fixtures/wireless/wireless_lans.json tests/integration/conftest.py tests/integration/test_circuits.py tests/integration/test_dcim.py tests/integration/test_ipam.py tests/unit/__init__.py tests/unit/test_detailendpoint.py tests/unit/test_endpoint.py tests/unit/test_endpoint_strict_filter.py tests/unit/test_extras.py tests/unit/test_file_upload.py tests/unit/test_multiformat_endpoint.py tests/unit/test_query.py tests/unit/test_request.py tests/unit/test_response.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619043.0 pynetbox-7.6.1/pynetbox.egg-info/dependency_links.txt0000644000175100017510000000000115136437143022462 0ustar00runnerrunner ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619043.0 pynetbox-7.6.1/pynetbox.egg-info/not-zip-safe0000644000175100017510000000000115136437143020642 0ustar00runnerrunner ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619043.0 pynetbox-7.6.1/pynetbox.egg-info/requires.txt0000644000175100017510000000004015136437143021006 0ustar00runnerrunnerrequests<3.0,>=2.20.0 packaging ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619043.0 pynetbox-7.6.1/pynetbox.egg-info/top_level.txt0000644000175100017510000000001115136437143021136 0ustar00runnerrunnerpynetbox ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/requirements-dev.txt0000644000175100017510000000015715136437134017115 0ustar00runnerrunnermkdocs-material==9.6.14 mkdocstrings[python]==0.29.1 pymdown-extensions>=10.0 pytest pytest-docker PyYAML ruff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/requirements.txt0000644000175100017510000000006215136437134016334 0ustar00runnerrunnerrequests>=2.32.3,<3.0 urllib3>=2.2.3,<3 packaging ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8233304 pynetbox-7.6.1/setup.cfg0000644000175100017510000000004615136437144014674 0ustar00runnerrunner[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/setup.py0000644000175100017510000000170015136437134014562 0ustar00runnerrunnerfrom setuptools import find_packages, setup setup( name="pynetbox", description="NetBox API client library", url="https://github.com/netbox-community/pynetbox", author="Zach Moody, Arthur Hanson", author_email="ahanson@netboxlabs.com", license="Apache2", include_package_data=True, use_scm_version=True, setup_requires=["setuptools_scm"], packages=find_packages(exclude=["tests", "tests.*"]), long_description=open("README.md").read(), long_description_content_type="text/markdown", install_requires=["requests>=2.20.0,<3.0", "packaging"], zip_safe=False, keywords=["netbox"], classifiers=[ "Intended Audience :: Developers", "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", ], ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8023305 pynetbox-7.6.1/tests/0000755000175100017510000000000015136437144014215 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/__init__.py0000644000175100017510000000000015136437134016313 0ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/conftest.py0000644000175100017510000000434115136437134016415 0ustar00runnerrunnerfrom urllib import parse import pytest from packaging import version DEFAULT_NETBOX_VERSIONS = "4.4" def pytest_addoption(parser): """Hook on the pytest option parser setup. Add some extra options to the parser. """ parser.addoption( "--netbox-versions", action="store", default=DEFAULT_NETBOX_VERSIONS, help=( "The versions of netbox to run integration tests against, as a" " comma-separated list. Default: %s" % DEFAULT_NETBOX_VERSIONS ), ) parser.addoption( "--no-cleanup", dest="cleanup", action="store_false", help=( "Skip any cleanup steps after the pytest session finishes. Any containers" " created will be left running and the docker-compose files used to" " create them will be left on disk." ), ) parser.addoption( "--url-override", dest="url_override", action="store", help=( "Overrides the URL to run tests to. This allows for testing to the same" " containers for separate runs." ), ) def pytest_configure(config): """Hook that runs after test collection is completed. Here we can modify items in the collected tests or parser args. """ # verify the netbox versions parse correctly and split them config.option.netbox_versions = [ version.Version(version_string) for version_string in config.option.netbox_versions.split(",") ] if "no:docker" in config.option.plugins and config.option.url_override: url_parse = parse.urlparse(config.option.url_override) class DockerServicesMock: def __init__(self, ports): self.ports = ports def wait_until_responsive(self, *args, **kwargs): return None def port_for(self, *args): return self.ports class Plugin: @pytest.fixture(scope="session") def docker_ip(self): return "127.0.0.1" @pytest.fixture(scope="session") def docker_services(self): return DockerServicesMock(url_parse.port) config.pluginmanager.register(Plugin()) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.7903304 pynetbox-7.6.1/tests/fixtures/0000755000175100017510000000000015136437144016066 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8023305 pynetbox-7.6.1/tests/fixtures/api/0000755000175100017510000000000015136437144016637 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/api/token_provision.json0000644000175100017510000000007215136437134022760 0ustar00runnerrunner{ "key": "1234567890123456789012345678901234567890" } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8043303 pynetbox-7.6.1/tests/fixtures/circuits/0000755000175100017510000000000015136437144017713 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/circuits/circuit.json0000644000175100017510000000075315136437134022254 0ustar00runnerrunner{ "id": 1, "cid": "123456", "provider": { "id": 1, "url": "http://localhost:8000/api/circuits/providers/1/", "name": "TEST", "slug": "test" }, "type": { "id": 1, "url": "http://localhost:8000/api/circuits/circuit-types/1/", "name": "Transit", "slug": "transit" }, "tenant": null, "install_date": null, "commit_rate": null, "description": "", "comments": "", "custom_fields": {} }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/circuits/circuit_termination.json0000644000175100017510000000166015136437134024663 0ustar00runnerrunner{ "id": 1, "circuit": { "id": 1, "url": "http://localhost:8000/api/circuits/circuits/1/", "cid": "123456" }, "term_side": "A", "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "interface": { "id": 4, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/0", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "TEST", "connection": null, "connected_interface": null }, "port_speed": 1000000, "upstream_speed": null, "xconnect_id": "", "pp_info": "" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/circuits/circuit_terminations.json0000644000175100017510000000247315136437134025051 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "circuit": { "id": 1, "url": "http://localhost:8000/api/circuits/circuits/1/", "cid": "123456" }, "term_side": "A", "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "interface": { "id": 4, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/0", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "TEST", "connection": null, "connected_interface": null }, "port_speed": 1000000, "upstream_speed": null, "xconnect_id": "", "pp_info": "" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/circuits/circuit_type.json0000644000175100017510000000007515136437134023312 0ustar00runnerrunner{ "id": 1, "name": "Transit", "slug": "transit" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/circuits/circuit_types.json0000644000175100017510000000027015136437134023472 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "Transit", "slug": "transit" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/circuits/circuits.json0000644000175100017510000000135615136437134022437 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "cid": "123456", "provider": { "id": 1, "url": "http://localhost:8000/api/circuits/providers/1/", "name": "TEST", "slug": "test" }, "type": { "id": 1, "url": "http://localhost:8000/api/circuits/circuit-types/1/", "name": "Transit", "slug": "transit" }, "tenant": null, "install_date": null, "commit_rate": null, "description": "", "comments": "", "custom_fields": {} } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/circuits/provider.json0000644000175100017510000000031615136437134022437 0ustar00runnerrunner{ "id": 1, "name": "TEST", "slug": "test", "asn": null, "account": "", "portal_url": "", "noc_contact": "", "admin_contact": "", "comments": "", "custom_fields": {} }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/circuits/providers.json0000644000175100017510000000060115136437134022617 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "TEST", "slug": "test", "asn": null, "account": "", "portal_url": "", "noc_contact": "", "admin_contact": "", "comments": "", "custom_fields": {} } ] }././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8133304 pynetbox-7.6.1/tests/fixtures/dcim/0000755000175100017510000000000015136437144017002 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/cable.json0000644000175100017510000000202615136437134020742 0ustar00runnerrunner{ "id": 1, "termination_a_type": "dcim.consoleport", "termination_a_id": 1, "termination_a": { "id": 1, "url": "http://localhost:8000/api/dcim/console-ports/1/", "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "tst1-test1", "display_name": "tst1-test1" }, "name": "Console", "cable": 1 }, "termination_b_type": "dcim.consoleserverport", "termination_b_id": 2, "termination_b": { "id": 2, "url": "http://localhost:8000/api/dcim/console-server-ports/2/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "tst1-test2", "display_name": "tst1-test2" }, "name": "Port 10", "cable": 1 }, "type": null, "status": { "value": true, "label": "Connected" }, "label": "", "color": "", "length": null, "length_unit": null }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/cables.json0000644000175100017510000001013015136437134021120 0ustar00runnerrunner{ "count": 3, "next": null, "previous": null, "results": [ { "id": 1, "termination_a_type": "dcim.consoleport", "termination_a_id": 1, "termination_a": { "id": 1, "url": "http://localhost:8000/api/dcim/console-ports/1/", "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "tst1-test1", "display_name": "tst1-test1" }, "name": "Console", "cable": 1 }, "termination_b_type": "dcim.consoleserverport", "termination_b_id": 2, "termination_b": { "id": 2, "url": "http://localhost:8000/api/dcim/console-server-ports/2/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "tst1-test2", "display_name": "tst1-test2" }, "name": "Port 10", "cable": 1 }, "type": null, "status": { "value": true, "label": "Connected" }, "label": "", "color": "", "length": null, "length_unit": null }, { "id": 2, "termination_a_type": "dcim.consoleport", "termination_a_id": 3, "termination_a": { "id": 3, "url": "http://localhost:8000/api/dcim/console-ports/3/", "device": { "id": 3, "url": "http://localhost:8000/api/dcim/devices/3/", "name": "tst1-test3", "display_name": "tst1-test3" }, "name": "Console", "cable": 2 }, "termination_b_type": "dcim.consoleserverport", "termination_b_id": 4, "termination_b": { "id": 4, "url": "http://localhost:8000/api/dcim/console-server-ports/4/", "device": { "id": 4, "url": "http://localhost:8000/api/dcim/devices/4/", "name": "tst1-test4", "display_name": "tst1-test4" }, "name": "Port 11", "cable": 2 }, "type": null, "status": { "value": true, "label": "Connected" }, "label": "", "color": "", "length": null, "length_unit": null }, { "id": 3, "termination_a_type": "dcim.consoleport", "termination_a_id": 5, "termination_a": { "id": 5, "url": "http://localhost:8000/api/dcim/console-ports/5/", "device": { "id": 5, "url": "http://localhost:8000/api/dcim/devices/5/", "name": "tst1-test5", "display_name": "tst1-test5" }, "name": "Console", "cable": 3 }, "termination_b_type": "dcim.consoleserverport", "termination_b_id": 6, "termination_b": { "id": 6, "url": "http://localhost:8000/api/dcim/console-server-ports/6/", "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "tst1-test6", "display_name": "tst1-test6" }, "name": "Port 1", "cable": 3 }, "type": null, "status": { "value": true, "label": "Connected" }, "label": "", "color": "", "length": null, "length_unit": null } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/choices.json0000644000175100017510000002330315136437134021312 0ustar00runnerrunner{ "device:face": [ { "label": "Front", "value": 0 }, { "label": "Rear", "value": 1 } ], "device:status": [ { "label": "Active", "value": 1 }, { "label": "Offline", "value": 0 }, { "label": "Planned", "value": 2 }, { "label": "Staged", "value": 3 }, { "label": "Failed", "value": 4 }, { "label": "Inventory", "value": 5 } ], "console-port:connection_status": [ { "label": "Planned", "value": false }, { "label": "Connected", "value": true } ], "interface:form_factor": [ { "label": "Virtual", "value": 0 }, { "label": "Link Aggregation Group (LAG)", "value": 200 }, { "label": "100BASE-TX (10/100ME)", "value": 800 }, { "label": "1000BASE-T (1GE)", "value": 1000 }, { "label": "10GBASE-T (10GE)", "value": 1150 }, { "label": "10GBASE-CX4 (10GE)", "value": 1170 }, { "label": "GBIC (1GE)", "value": 1050 }, { "label": "SFP (1GE)", "value": 1100 }, { "label": "SFP+ (10GE)", "value": 1200 }, { "label": "XFP (10GE)", "value": 1300 }, { "label": "XENPAK (10GE)", "value": 1310 }, { "label": "X2 (10GE)", "value": 1320 }, { "label": "SFP28 (25GE)", "value": 1350 }, { "label": "QSFP+ (40GE)", "value": 1400 }, { "label": "CFP (100GE)", "value": 1500 }, { "label": "CFP2 (100GE)", "value": 1510 }, { "label": "CFP4 (100GE)", "value": 1520 }, { "label": "Cisco CPAK (100GE)", "value": 1550 }, { "label": "QSFP28 (100GE)", "value": 1600 }, { "label": "IEEE 802.11a", "value": 2600 }, { "label": "IEEE 802.11b/g", "value": 2610 }, { "label": "IEEE 802.11n", "value": 2620 }, { "label": "IEEE 802.11ac", "value": 2630 }, { "label": "IEEE 802.11ad", "value": 2640 }, { "label": "SFP (1GFC)", "value": 3010 }, { "label": "SFP (2GFC)", "value": 3020 }, { "label": "SFP (4GFC)", "value": 3040 }, { "label": "SFP+ (8GFC)", "value": 3080 }, { "label": "SFP+ (16GFC)", "value": 3160 }, { "label": "T1 (1.544 Mbps)", "value": 4000 }, { "label": "E1 (2.048 Mbps)", "value": 4010 }, { "label": "T3 (45 Mbps)", "value": 4040 }, { "label": "E3 (34 Mbps)", "value": 4050 }, { "label": "Cisco StackWise", "value": 5000 }, { "label": "Cisco StackWise Plus", "value": 5050 }, { "label": "Cisco FlexStack", "value": 5100 }, { "label": "Cisco FlexStack Plus", "value": 5150 }, { "label": "Juniper VCP", "value": 5200 }, { "label": "Extreme SummitStack", "value": 5300 }, { "label": "Extreme SummitStack-128", "value": 5310 }, { "label": "Extreme SummitStack-256", "value": 5320 }, { "label": "Extreme SummitStack-512", "value": 5330 }, { "label": "Other", "value": 32767 } ], "interface:mode": [ { "label": "Access", "value": 100 }, { "label": "Tagged", "value": 200 }, { "label": "Tagged All", "value": 300 } ], "interface-connection:connection_status": [ { "label": "Planned", "value": false }, { "label": "Connected", "value": true } ], "interface-template:form_factor": [ { "label": "Virtual", "value": 0 }, { "label": "Link Aggregation Group (LAG)", "value": 200 }, { "label": "100BASE-TX (10/100ME)", "value": 800 }, { "label": "1000BASE-T (1GE)", "value": 1000 }, { "label": "10GBASE-T (10GE)", "value": 1150 }, { "label": "10GBASE-CX4 (10GE)", "value": 1170 }, { "label": "GBIC (1GE)", "value": 1050 }, { "label": "SFP (1GE)", "value": 1100 }, { "label": "SFP+ (10GE)", "value": 1200 }, { "label": "XFP (10GE)", "value": 1300 }, { "label": "XENPAK (10GE)", "value": 1310 }, { "label": "X2 (10GE)", "value": 1320 }, { "label": "SFP28 (25GE)", "value": 1350 }, { "label": "QSFP+ (40GE)", "value": 1400 }, { "label": "CFP (100GE)", "value": 1500 }, { "label": "CFP2 (100GE)", "value": 1510 }, { "label": "CFP4 (100GE)", "value": 1520 }, { "label": "Cisco CPAK (100GE)", "value": 1550 }, { "label": "QSFP28 (100GE)", "value": 1600 }, { "label": "IEEE 802.11a", "value": 2600 }, { "label": "IEEE 802.11b/g", "value": 2610 }, { "label": "IEEE 802.11n", "value": 2620 }, { "label": "IEEE 802.11ac", "value": 2630 }, { "label": "IEEE 802.11ad", "value": 2640 }, { "label": "SFP (1GFC)", "value": 3010 }, { "label": "SFP (2GFC)", "value": 3020 }, { "label": "SFP (4GFC)", "value": 3040 }, { "label": "SFP+ (8GFC)", "value": 3080 }, { "label": "SFP+ (16GFC)", "value": 3160 }, { "label": "T1 (1.544 Mbps)", "value": 4000 }, { "label": "E1 (2.048 Mbps)", "value": 4010 }, { "label": "T3 (45 Mbps)", "value": 4040 }, { "label": "E3 (34 Mbps)", "value": 4050 }, { "label": "Cisco StackWise", "value": 5000 }, { "label": "Cisco StackWise Plus", "value": 5050 }, { "label": "Cisco FlexStack", "value": 5100 }, { "label": "Cisco FlexStack Plus", "value": 5150 }, { "label": "Juniper VCP", "value": 5200 }, { "label": "Extreme SummitStack", "value": 5300 }, { "label": "Extreme SummitStack-128", "value": 5310 }, { "label": "Extreme SummitStack-256", "value": 5320 }, { "label": "Extreme SummitStack-512", "value": 5330 }, { "label": "Other", "value": 32767 } ], "power-port:connection_status": [ { "label": "Planned", "value": false }, { "label": "Connected", "value": true } ], "rack:type": [ { "label": "2-post frame", "value": 100 }, { "label": "4-post frame", "value": 200 }, { "label": "4-post cabinet", "value": 300 }, { "label": "Wall-mounted frame", "value": 1000 }, { "label": "Wall-mounted cabinet", "value": 1100 } ], "rack:width": [ { "label": "19 inches", "value": 19 }, { "label": "23 inches", "value": 23 } ], "site:status": [ { "label": "Active", "value": 1 }, { "label": "Planned", "value": 2 }, { "label": "Retired", "value": 4 } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/console_port.json0000644000175100017510000000102615136437134022401 0ustar00runnerrunner{ "id": 1, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "Console (RE0)", "cs_port": { "id": 27, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 3", "connected_console": 1 }, "connection_status": true }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/console_port_template.json0000644000175100017510000000061115136437134024273 0ustar00runnerrunner{ "id": 1, "device_type": { "id": 1, "url": "http://localhost:8000/api/dcim/device-types/1/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960" }, "name": "Console (RE0)" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/console_port_templates.json0000644000175100017510000000726715136437134024474 0ustar00runnerrunner{ "count": 7, "next": null, "previous": null, "results": [ { "id": 3, "device_type": { "id": 2, "url": "http://localhost:8000/api/dcim/device-types/2/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "EX9214", "slug": "ex9214" }, "name": "Console (RE0)" }, { "id": 4, "device_type": { "id": 2, "url": "http://localhost:8000/api/dcim/device-types/2/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "EX9214", "slug": "ex9214" }, "name": "Console (RE1)" }, { "id": 1, "device_type": { "id": 1, "url": "http://localhost:8000/api/dcim/device-types/1/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960" }, "name": "Console (RE0)" }, { "id": 2, "device_type": { "id": 1, "url": "http://localhost:8000/api/dcim/device-types/1/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960" }, "name": "Console (RE1)" }, { "id": 5, "device_type": { "id": 3, "url": "http://localhost:8000/api/dcim/device-types/3/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-24Q", "slug": "qfx5100-24q" }, "name": "Console" }, { "id": 6, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Console" }, { "id": 7, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "Serial" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/console_ports.json0000644000175100017510000001664015136437134022574 0ustar00runnerrunner{ "count": 15, "next": null, "previous": null, "results": [ { "id": 3, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "Console (RE0)", "cs_port": { "id": 5, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 1", "connected_console": 3 }, "connection_status": true }, { "id": 4, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "Console (RE1)", "cs_port": { "id": 16, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 2", "connected_console": 4 }, "connection_status": true }, { "id": 11, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "Console (RE0)", "cs_port": null, "connection_status": true }, { "id": 12, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "Console (RE1)", "cs_port": null, "connection_status": true }, { "id": 1, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "Console (RE0)", "cs_port": { "id": 27, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 3", "connected_console": 1 }, "connection_status": true }, { "id": 2, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "Console (RE1)", "cs_port": { "id": 38, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 4", "connected_console": 2 }, "connection_status": true }, { "id": 9, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "Console (RE0)", "cs_port": null, "connection_status": true }, { "id": 10, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "Console (RE1)", "cs_port": null, "connection_status": true }, { "id": 6, "device": { "id": 4, "url": "http://localhost:8000/api/dcim/devices/4/", "name": "test1-leaf1", "display_name": "test1-leaf1" }, "name": "Console", "cs_port": { "id": 48, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 5", "connected_console": 6 }, "connection_status": true }, { "id": 7, "device": { "id": 5, "url": "http://localhost:8000/api/dcim/devices/5/", "name": "test1-leaf2", "display_name": "test1-leaf2" }, "name": "Console", "cs_port": null, "connection_status": true }, { "id": 13, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Console", "cs_port": null, "connection_status": true }, { "id": 15, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "Serial", "cs_port": null, "connection_status": true }, { "id": 16, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "Serial", "cs_port": null, "connection_status": true }, { "id": 5, "device": { "id": 3, "url": "http://localhost:8000/api/dcim/devices/3/", "name": "test1-spine1", "display_name": "test1-spine1" }, "name": "Console", "cs_port": { "id": 49, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 6", "connected_console": 5 }, "connection_status": true }, { "id": 8, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "Console", "cs_port": null, "connection_status": true } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/console_server_port.json0000644000175100017510000000035015136437134023766 0ustar00runnerrunner{ "id": 1, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 1", "connected_console": 3 }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/console_server_port_template.json0000644000175100017510000000061715136437134025667 0ustar00runnerrunner{ "id": 1, "device_type": { "id": 3, "url": "http://localhost:8000/api/dcim/device-types/3/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-24Q", "slug": "qfx5100-24q" }, "name": "Console" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/console_server_port_templates.json0000644000175100017510000006310515136437134026053 0ustar00runnerrunner{ "count": 50, "next": null, "previous": null, "results": [ { "id": 1, "device_type": { "id": 3, "url": "http://localhost:8000/api/dcim/device-types/3/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-24Q", "slug": "qfx5100-24q" }, "name": "Console" }, { "id": 3, "device_type": { "id": 4, "url": "http://localhost:8000/api/dcim/device-types/4/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-48S", "slug": "qfx5100-48s" }, "name": "Console" }, { "id": 4, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 1" }, { "id": 13, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 10" }, { "id": 14, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 11" }, { "id": 15, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 12" }, { "id": 16, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 13" }, { "id": 17, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 14" }, { "id": 18, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 15" }, { "id": 19, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 16" }, { "id": 20, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 17" }, { "id": 21, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 18" }, { "id": 22, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 19" }, { "id": 5, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 2" }, { "id": 23, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 20" }, { "id": 24, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 21" }, { "id": 25, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 22" }, { "id": 26, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 23" }, { "id": 27, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 24" }, { "id": 28, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 25" }, { "id": 29, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 26" }, { "id": 30, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 27" }, { "id": 31, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 28" }, { "id": 32, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 29" }, { "id": 6, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 3" }, { "id": 33, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 30" }, { "id": 34, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 31" }, { "id": 35, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 32" }, { "id": 36, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 33" }, { "id": 37, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 34" }, { "id": 38, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 35" }, { "id": 39, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 36" }, { "id": 40, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 37" }, { "id": 41, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 38" }, { "id": 42, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 39" }, { "id": 7, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 4" }, { "id": 43, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 40" }, { "id": 44, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 41" }, { "id": 45, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 42" }, { "id": 46, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 43" }, { "id": 47, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 44" }, { "id": 48, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 45" }, { "id": 49, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 46" }, { "id": 50, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 47" }, { "id": 51, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 48" }, { "id": 8, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 5" }, { "id": 9, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 6" }, { "id": 10, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 7" }, { "id": 11, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 8" }, { "id": 12, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "Port 9" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/console_server_ports.json0000644000175100017510000003660215136437134024162 0ustar00runnerrunner{ "count": 48, "next": null, "previous": null, "results": [ { "id": 5, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 1", "connected_console": 3 }, { "id": 16, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 2", "connected_console": 4 }, { "id": 27, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 3", "connected_console": 1 }, { "id": 38, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 4", "connected_console": 2 }, { "id": 48, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 5", "connected_console": 6 }, { "id": 49, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 6", "connected_console": 5 }, { "id": 50, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 7", "connected_console": null }, { "id": 51, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 8", "connected_console": null }, { "id": 52, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 9", "connected_console": null }, { "id": 6, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 10", "connected_console": null }, { "id": 7, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 11", "connected_console": null }, { "id": 8, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 12", "connected_console": null }, { "id": 9, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 13", "connected_console": null }, { "id": 10, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 14", "connected_console": null }, { "id": 11, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 15", "connected_console": null }, { "id": 12, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 16", "connected_console": null }, { "id": 13, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 17", "connected_console": null }, { "id": 14, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 18", "connected_console": null }, { "id": 15, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 19", "connected_console": null }, { "id": 17, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 20", "connected_console": null }, { "id": 18, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 21", "connected_console": null }, { "id": 19, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 22", "connected_console": null }, { "id": 20, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 23", "connected_console": null }, { "id": 21, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 24", "connected_console": null }, { "id": 22, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 25", "connected_console": null }, { "id": 23, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 26", "connected_console": null }, { "id": 24, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 27", "connected_console": null }, { "id": 25, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 28", "connected_console": null }, { "id": 26, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 29", "connected_console": null }, { "id": 28, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 30", "connected_console": null }, { "id": 29, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 31", "connected_console": null }, { "id": 30, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 32", "connected_console": null }, { "id": 31, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 33", "connected_console": null }, { "id": 32, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 34", "connected_console": null }, { "id": 33, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 35", "connected_console": null }, { "id": 34, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 36", "connected_console": null }, { "id": 35, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 37", "connected_console": null }, { "id": 36, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 38", "connected_console": null }, { "id": 37, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 39", "connected_console": null }, { "id": 39, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 40", "connected_console": null }, { "id": 40, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 41", "connected_console": null }, { "id": 41, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 42", "connected_console": null }, { "id": 42, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 43", "connected_console": null }, { "id": 43, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 44", "connected_console": null }, { "id": 44, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 45", "connected_console": null }, { "id": 45, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 46", "connected_console": null }, { "id": 46, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 47", "connected_console": null }, { "id": 47, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "Port 48", "connected_console": null } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/device.json0000644000175100017510000000345215136437134021137 0ustar00runnerrunner{ "id": 1, "name": "test1-edge1", "display_name": "test1-edge1", "device_type": { "id": 1, "url": "http://localhost:8000/api/dcim/device-types/1/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960" }, "role": { "id": 1, "url": "http://localhost:8000/api/dcim/device-roles/1/", "name": "Router", "slug": "router" }, "tenant": null, "platform": { "id": 1, "url": "http://localhost:8000/api/dcim/platforms/1/", "name": "Juniper Junos", "slug": "juniper-junos" }, "serial": "5555555555", "asset_tag": null, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "rack": { "id": 1, "url": "http://localhost:8000/api/dcim/racks/1/", "name": "A1R1", "display_name": "A1R1 (T23A01)" }, "position": 1, "face": { "value": 0, "label": "Front" }, "parent_device": null, "status": { "value": true, "label": "Active" }, "primary_ip": { "id": 1, "url": "http://localhost:8000/api/ipam/ip-addresses/1/", "family": 4, "address": "10.0.255.1/32" }, "primary_ip4": { "id": 1, "url": "http://localhost:8000/api/ipam/ip-addresses/1/", "family": 4, "address": "10.0.255.1/32" }, "primary_ip6": null, "comments": "", "local_context_data": { "testing": "test" }, "custom_fields": {}, "config_context": { "test_key": "test_val" } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/device_bay.json0000644000175100017510000000037115136437134021767 0ustar00runnerrunner{ "id": 1, "device": { "id": 13, "url": "http://localhost:8000/api/dcim/devices/13/", "name": "test1-mothership", "display_name": "test1-mothership" }, "name": "thing-1", "installed_device": null }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/device_bay_template.json0000644000175100017510000000062315136437134023662 0ustar00runnerrunner{ "id": 1, "device_type": { "id": 7, "url": "http://localhost:8000/api/dcim/device-types/7/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "Mothership", "slug": "mothership" }, "name": "thing-1" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/device_bay_templates.json0000644000175100017510000000322015136437134024041 0ustar00runnerrunner{ "count": 3, "next": null, "previous": null, "results": [ { "id": 1, "device_type": { "id": 7, "url": "http://localhost:8000/api/dcim/device-types/7/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "Mothership", "slug": "mothership" }, "name": "thing-1" }, { "id": 2, "device_type": { "id": 7, "url": "http://localhost:8000/api/dcim/device-types/7/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "Mothership", "slug": "mothership" }, "name": "thing-2" }, { "id": 3, "device_type": { "id": 7, "url": "http://localhost:8000/api/dcim/device-types/7/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "Mothership", "slug": "mothership" }, "name": "thing-3" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/device_bays.json0000644000175100017510000000211215136437134022145 0ustar00runnerrunner{ "count": 3, "next": null, "previous": null, "results": [ { "id": 1, "device": { "id": 13, "url": "http://localhost:8000/api/dcim/devices/13/", "name": "test1-mothership", "display_name": "test1-mothership" }, "name": "thing-1", "installed_device": null }, { "id": 2, "device": { "id": 13, "url": "http://localhost:8000/api/dcim/devices/13/", "name": "test1-mothership", "display_name": "test1-mothership" }, "name": "thing-2", "installed_device": null }, { "id": 3, "device": { "id": 13, "url": "http://localhost:8000/api/dcim/devices/13/", "name": "test1-mothership", "display_name": "test1-mothership" }, "name": "thing-3", "installed_device": null } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/device_bulk_create.json0000644000175100017510000000151415136437134023474 0ustar00runnerrunner[ { "id": 1, "name": "test1-core3", "device_type": 1, "role": 3, "tenant": null, "platform": null, "serial": "", "asset_tag": null, "site": 1, "rack": null, "position": null, "face": null, "status": 1, "primary_ip4": null, "primary_ip6": null, "cluster": null, "comments": "" }, { "id": 2, "name": "test1-core4", "device_type": 1, "role": 3, "tenant": null, "platform": null, "serial": "", "asset_tag": null, "site": 1, "rack": null, "position": null, "face": null, "status": 1, "primary_ip4": null, "primary_ip6": null, "cluster": null, "comments": "" } ]././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/device_role.json0000644000175100017510000000012215136437134022147 0ustar00runnerrunner{ "id": 1, "name": "Router", "slug": "router", "color": "purple" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/device_roles.json0000644000175100017510000000160515136437134022341 0ustar00runnerrunner{ "count": 6, "next": null, "previous": null, "results": [ { "id": 3, "name": "Core Switch", "slug": "core-switch", "color": "red" }, { "id": 4, "name": "Leaf Switch", "slug": "leaf-switch", "color": "teal" }, { "id": 5, "name": "OOB Switch", "slug": "oob-switch", "color": "purple" }, { "id": 6, "name": "PDU", "slug": "pdu", "color": "yellow" }, { "id": 1, "name": "Router", "slug": "router", "color": "purple" }, { "id": 2, "name": "Spine Switch", "slug": "spine-switch", "color": "green" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/device_type.json0000644000175100017510000000106315136437134022174 0ustar00runnerrunner{ "id": 1, "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960", "part_number": "", "u_height": 16, "is_full_depth": true, "interface_ordering": { "value": 1, "label": "Slot/position" }, "is_console_server": false, "is_pdu": false, "is_network_device": true, "subdevice_role": null, "comments": "", "custom_fields": {}, "instance_count": 2 }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/device_types.json0000644000175100017510000001116215136437134022360 0ustar00runnerrunner{ "count": 6, "next": null, "previous": null, "results": [ { "id": 2, "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "EX9214", "slug": "ex9214", "part_number": "", "u_height": 16, "is_full_depth": true, "interface_ordering": { "value": 1, "label": "Slot/position" }, "is_console_server": false, "is_pdu": false, "is_network_device": true, "subdevice_role": null, "comments": "", "custom_fields": {}, "instance_count": 2 }, { "id": 1, "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960", "part_number": "", "u_height": 16, "is_full_depth": true, "interface_ordering": { "value": 1, "label": "Slot/position" }, "is_console_server": false, "is_pdu": false, "is_network_device": true, "subdevice_role": null, "comments": "", "custom_fields": {}, "instance_count": 2 }, { "id": 3, "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-24Q", "slug": "qfx5100-24q", "part_number": "", "u_height": 1, "is_full_depth": true, "interface_ordering": { "value": 1, "label": "Slot/position" }, "is_console_server": false, "is_pdu": false, "is_network_device": true, "subdevice_role": null, "comments": "", "custom_fields": {}, "instance_count": 2 }, { "id": 4, "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-48S", "slug": "qfx5100-48s", "part_number": "", "u_height": 1, "is_full_depth": true, "interface_ordering": { "value": 1, "label": "Slot/position" }, "is_console_server": false, "is_pdu": false, "is_network_device": true, "subdevice_role": null, "comments": "", "custom_fields": {}, "instance_count": 2 }, { "id": 5, "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148", "part_number": "", "u_height": 1, "is_full_depth": true, "interface_ordering": { "value": 1, "label": "Slot/position" }, "is_console_server": true, "is_pdu": false, "is_network_device": false, "subdevice_role": null, "comments": "", "custom_fields": {}, "instance_count": 1 }, { "id": 6, "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9", "part_number": "", "u_height": 0, "is_full_depth": false, "interface_ordering": { "value": 1, "label": "Slot/position" }, "is_console_server": false, "is_pdu": true, "is_network_device": false, "subdevice_role": null, "comments": "", "custom_fields": {}, "instance_count": 2 } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/devices.json0000644000175100017510000005305515136437134021326 0ustar00runnerrunner{ "count": 11, "next": null, "previous": null, "results": [ { "id": 2, "name": "test1-core1", "display_name": "test1-core1", "device_type": { "id": 2, "url": "http://localhost:8000/api/dcim/device-types/2/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "EX9214", "slug": "ex9214" }, "role": { "id": 3, "url": "http://localhost:8000/api/dcim/device-roles/3/", "name": "Core Switch", "slug": "core-switch" }, "tenant": null, "platform": { "id": 1, "url": "http://localhost:8000/api/dcim/platforms/1/", "name": "Juniper Junos", "slug": "juniper-junos" }, "serial": "", "asset_tag": null, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "rack": { "id": 1, "url": "http://localhost:8000/api/dcim/racks/1/", "name": "A1R1", "display_name": "A1R1 (T23A01)" }, "position": 17, "face": { "value": 0, "label": "Front" }, "parent_device": null, "status": { "value": true, "label": "Active" }, "primary_ip": { "id": 5, "url": "http://localhost:8000/api/ipam/ip-addresses/5/", "family": 4, "address": "10.0.254.1/24" }, "primary_ip4": { "id": 5, "url": "http://localhost:8000/api/ipam/ip-addresses/5/", "family": 4, "address": "10.0.254.1/24" }, "primary_ip6": null, "comments": "", "custom_fields": {} }, { "id": 8, "name": "test1-core2", "display_name": "test1-core2", "device_type": { "id": 2, "url": "http://localhost:8000/api/dcim/device-types/2/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "EX9214", "slug": "ex9214" }, "role": { "id": 3, "url": "http://localhost:8000/api/dcim/device-roles/3/", "name": "Core Switch", "slug": "core-switch" }, "tenant": null, "platform": { "id": 1, "url": "http://localhost:8000/api/dcim/platforms/1/", "name": "Juniper Junos", "slug": "juniper-junos" }, "serial": "67856734534", "asset_tag": null, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "rack": { "id": 2, "url": "http://localhost:8000/api/dcim/racks/2/", "name": "A1R2", "display_name": "A1R2 (T24A01)" }, "position": 17, "face": { "value": 0, "label": "Front" }, "parent_device": null, "status": { "value": true, "label": "Active" }, "primary_ip": { "id": 19, "url": "http://localhost:8000/api/ipam/ip-addresses/19/", "family": 4, "address": "10.0.254.2/32" }, "primary_ip4": { "id": 19, "url": "http://localhost:8000/api/ipam/ip-addresses/19/", "family": 4, "address": "10.0.254.2/32" }, "primary_ip6": null, "comments": "", "custom_fields": {} }, { "id": 1, "name": "test1-edge1", "display_name": "test1-edge1", "device_type": { "id": 1, "url": "http://localhost:8000/api/dcim/device-types/1/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960" }, "role": { "id": 1, "url": "http://localhost:8000/api/dcim/device-roles/1/", "name": "Router", "slug": "router" }, "tenant": null, "platform": { "id": 1, "url": "http://localhost:8000/api/dcim/platforms/1/", "name": "Juniper Junos", "slug": "juniper-junos" }, "serial": "5555555555", "asset_tag": null, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "rack": { "id": 1, "url": "http://localhost:8000/api/dcim/racks/1/", "name": "A1R1", "display_name": "A1R1 (T23A01)" }, "position": 1, "face": { "value": 0, "label": "Front" }, "parent_device": null, "status": { "value": true, "label": "Active" }, "primary_ip": { "id": 1, "url": "http://localhost:8000/api/ipam/ip-addresses/1/", "family": 4, "address": "10.0.255.1/32" }, "primary_ip4": { "id": 1, "url": "http://localhost:8000/api/ipam/ip-addresses/1/", "family": 4, "address": "10.0.255.1/32" }, "primary_ip6": null, "comments": "", "custom_fields": {} }, { "id": 7, "name": "test1-edge2", "display_name": "test1-edge2", "device_type": { "id": 1, "url": "http://localhost:8000/api/dcim/device-types/1/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960" }, "role": { "id": 1, "url": "http://localhost:8000/api/dcim/device-roles/1/", "name": "Router", "slug": "router" }, "tenant": null, "platform": { "id": 1, "url": "http://localhost:8000/api/dcim/platforms/1/", "name": "Juniper Junos", "slug": "juniper-junos" }, "serial": "7567356345", "asset_tag": null, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "rack": { "id": 2, "url": "http://localhost:8000/api/dcim/racks/2/", "name": "A1R2", "display_name": "A1R2 (T24A01)" }, "position": 1, "face": { "value": 0, "label": "Front" }, "parent_device": null, "status": { "value": true, "label": "Active" }, "primary_ip": { "id": 3, "url": "http://localhost:8000/api/ipam/ip-addresses/3/", "family": 4, "address": "10.0.255.2/32" }, "primary_ip4": { "id": 3, "url": "http://localhost:8000/api/ipam/ip-addresses/3/", "family": 4, "address": "10.0.255.2/32" }, "primary_ip6": null, "comments": "", "custom_fields": {} }, { "id": 4, "name": "test1-leaf1", "display_name": "test1-leaf1", "device_type": { "id": 4, "url": "http://localhost:8000/api/dcim/device-types/4/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-48S", "slug": "qfx5100-48s" }, "role": { "id": 4, "url": "http://localhost:8000/api/dcim/device-roles/4/", "name": "Leaf Switch", "slug": "leaf-switch" }, "tenant": null, "platform": { "id": 1, "url": "http://localhost:8000/api/dcim/platforms/1/", "name": "Juniper Junos", "slug": "juniper-junos" }, "serial": "", "asset_tag": null, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "rack": { "id": 1, "url": "http://localhost:8000/api/dcim/racks/1/", "name": "A1R1", "display_name": "A1R1 (T23A01)" }, "position": 34, "face": { "value": 0, "label": "Front" }, "parent_device": null, "status": { "value": true, "label": "Active" }, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "comments": "", "custom_fields": {} }, { "id": 5, "name": "test1-leaf2", "display_name": "test1-leaf2", "device_type": { "id": 4, "url": "http://localhost:8000/api/dcim/device-types/4/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-48S", "slug": "qfx5100-48s" }, "role": { "id": 4, "url": "http://localhost:8000/api/dcim/device-roles/4/", "name": "Leaf Switch", "slug": "leaf-switch" }, "tenant": null, "platform": { "id": 1, "url": "http://localhost:8000/api/dcim/platforms/1/", "name": "Juniper Junos", "slug": "juniper-junos" }, "serial": "9823478293748", "asset_tag": null, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "rack": { "id": 2, "url": "http://localhost:8000/api/dcim/racks/2/", "name": "A1R2", "display_name": "A1R2 (T24A01)" }, "position": 34, "face": { "value": 0, "label": "Front" }, "parent_device": null, "status": { "value": true, "label": "Active" }, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "comments": "", "custom_fields": {} }, { "id": 9, "name": "test1-oob1", "display_name": "test1-oob1", "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "role": { "id": 5, "url": "http://localhost:8000/api/dcim/device-roles/5/", "name": "OOB Switch", "slug": "oob-switch" }, "tenant": null, "platform": { "id": 2, "url": "http://localhost:8000/api/dcim/platforms/2/", "name": "Opengear", "slug": "opengear" }, "serial": "98273942938", "asset_tag": null, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "rack": { "id": 1, "url": "http://localhost:8000/api/dcim/racks/1/", "name": "A1R1", "display_name": "A1R1 (T23A01)" }, "position": 42, "face": { "value": 0, "label": "Front" }, "parent_device": null, "status": { "value": true, "label": "Active" }, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "comments": "", "custom_fields": {} }, { "id": 11, "name": "test1-pdu1", "display_name": "test1-pdu1", "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "role": { "id": 6, "url": "http://localhost:8000/api/dcim/device-roles/6/", "name": "PDU", "slug": "pdu" }, "tenant": null, "platform": null, "serial": "", "asset_tag": null, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "rack": { "id": 1, "url": "http://localhost:8000/api/dcim/racks/1/", "name": "A1R1", "display_name": "A1R1 (T23A01)" }, "position": null, "face": null, "parent_device": null, "status": { "value": true, "label": "Active" }, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "comments": "", "custom_fields": {} }, { "id": 12, "name": "test1-pdu2", "display_name": "test1-pdu2", "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "role": { "id": 6, "url": "http://localhost:8000/api/dcim/device-roles/6/", "name": "PDU", "slug": "pdu" }, "tenant": null, "platform": null, "serial": "", "asset_tag": null, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "rack": { "id": 2, "url": "http://localhost:8000/api/dcim/racks/2/", "name": "A1R2", "display_name": "A1R2 (T24A01)" }, "position": null, "face": null, "parent_device": null, "status": { "value": true, "label": "Active" }, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "comments": "", "custom_fields": {} }, { "id": 3, "name": "test1-spine1", "display_name": "test1-spine1", "device_type": { "id": 3, "url": "http://localhost:8000/api/dcim/device-types/3/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-24Q", "slug": "qfx5100-24q" }, "role": { "id": 2, "url": "http://localhost:8000/api/dcim/device-roles/2/", "name": "Spine Switch", "slug": "spine-switch" }, "tenant": null, "platform": { "id": 1, "url": "http://localhost:8000/api/dcim/platforms/1/", "name": "Juniper Junos", "slug": "juniper-junos" }, "serial": "", "asset_tag": null, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "rack": { "id": 1, "url": "http://localhost:8000/api/dcim/racks/1/", "name": "A1R1", "display_name": "A1R1 (T23A01)" }, "position": 33, "face": { "value": 0, "label": "Front" }, "parent_device": null, "status": { "value": true, "label": "Active" }, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "comments": "", "custom_fields": {} }, { "id": 6, "name": "test1-spine2", "display_name": "test1-spine2", "device_type": { "id": 3, "url": "http://localhost:8000/api/dcim/device-types/3/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-24Q", "slug": "qfx5100-24q" }, "role": { "id": 2, "url": "http://localhost:8000/api/dcim/device-roles/2/", "name": "Spine Switch", "slug": "spine-switch" }, "tenant": null, "platform": { "id": 1, "url": "http://localhost:8000/api/dcim/platforms/1/", "name": "Juniper Junos", "slug": "juniper-junos" }, "serial": "45649818158", "asset_tag": null, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "rack": { "id": 2, "url": "http://localhost:8000/api/dcim/racks/2/", "name": "A1R2", "display_name": "A1R2 (T24A01)" }, "position": 33, "face": { "value": 0, "label": "Front" }, "parent_device": null, "status": { "value": true, "label": "Active" }, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "comments": "", "custom_fields": {} } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/interface.json0000644000175100017510000000272415136437134021641 0ustar00runnerrunner{ "id": 1, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/0/0", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "enabled": true, "lag": { "id": 223, "url": "http://localhost:8000/api/dcim/interfaces/223/", "name": "ae0" }, "mtu": null, "mac_address": null, "mgmt_only": false, "description": "", "is_connected": true, "connected_endpoints": [ { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "display": "tst-endpoint", "name": "tst-endpoint" } ], "connected_endpoints_type": "dcim.device", "connected_endpoints_reachable": true, "cable": { "id": 1, "url": "http://localhost:8000/api/dcim/cables/1/", "label": "" }, "mode": { "value": "tagged", "label": "Tagged", "id": 200 }, "untagged_vlan": { "id": 2, "url": "http://localhost:8000/api/ipam/vlans/792/", "vid": 2069, "name": "v2069", "display_name": "2069 (v2069)" }, "tagged_vlans": [ { "id": 3, "url": "http://localhost:8000/api/ipam/vlans/248/", "vid": 1210, "name": "v1210", "display_name": "1210 (v1210)" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/interface_connection.json0000644000175100017510000000173715136437134024063 0ustar00runnerrunner{ "id": 1, "interface_a": { "id": 99, "url": "http://localhost:8000/api/dcim/interfaces/99/", "device": { "id": 5, "url": "http://localhost:8000/api/dcim/devices/5/", "name": "test1-leaf2", "display_name": "test1-leaf2" }, "name": "et-0/0/48", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 15, "url": "http://localhost:8000/api/dcim/interfaces/15/", "device": { "id": 3, "url": "http://localhost:8000/api/dcim/devices/3/", "name": "test1-spine1", "display_name": "test1-spine1" }, "name": "et-0/0/1", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/interface_connections.json0000644000175100017510000005276015136437134024250 0ustar00runnerrunner{ "count": 17, "next": null, "previous": null, "results": [ { "id": 22, "interface_a": { "id": 9, "url": "http://localhost:8000/api/dcim/interfaces/9/", "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/5", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 218, "url": "http://localhost:8000/api/dcim/interfaces/218/", "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/5", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 23, "interface_a": { "id": 8, "url": "http://localhost:8000/api/dcim/interfaces/8/", "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/4", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 206, "url": "http://localhost:8000/api/dcim/interfaces/206/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/5", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 24, "interface_a": { "id": 7, "url": "http://localhost:8000/api/dcim/interfaces/7/", "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/3", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 212, "url": "http://localhost:8000/api/dcim/interfaces/212/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/5", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 21, "interface_a": { "id": 194, "url": "http://localhost:8000/api/dcim/interfaces/194/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/1/2", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 200, "url": "http://localhost:8000/api/dcim/interfaces/200/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/1/2", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 17, "interface_a": { "id": 192, "url": "http://localhost:8000/api/dcim/interfaces/192/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/1/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 175, "url": "http://localhost:8000/api/dcim/interfaces/175/", "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/1/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 20, "interface_a": { "id": 191, "url": "http://localhost:8000/api/dcim/interfaces/191/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/0/2", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 197, "url": "http://localhost:8000/api/dcim/interfaces/197/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/0/2", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 16, "interface_a": { "id": 189, "url": "http://localhost:8000/api/dcim/interfaces/189/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/0/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 37, "url": "http://localhost:8000/api/dcim/interfaces/37/", "device": { "id": 3, "url": "http://localhost:8000/api/dcim/devices/3/", "name": "test1-spine1", "display_name": "test1-spine1" }, "name": "et-0/1/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 8, "interface_a": { "id": 92, "url": "http://localhost:8000/api/dcim/interfaces/92/", "device": { "id": 4, "url": "http://localhost:8000/api/dcim/devices/4/", "name": "test1-leaf1", "display_name": "test1-leaf1" }, "name": "xe-0/0/47", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 145, "url": "http://localhost:8000/api/dcim/interfaces/145/", "device": { "id": 5, "url": "http://localhost:8000/api/dcim/devices/5/", "name": "test1-leaf2", "display_name": "test1-leaf2" }, "name": "xe-0/0/47", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 7, "interface_a": { "id": 91, "url": "http://localhost:8000/api/dcim/interfaces/91/", "device": { "id": 4, "url": "http://localhost:8000/api/dcim/devices/4/", "name": "test1-leaf1", "display_name": "test1-leaf1" }, "name": "xe-0/0/46", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 144, "url": "http://localhost:8000/api/dcim/interfaces/144/", "device": { "id": 5, "url": "http://localhost:8000/api/dcim/devices/5/", "name": "test1-leaf2", "display_name": "test1-leaf2" }, "name": "xe-0/0/46", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 6, "interface_a": { "id": 47, "url": "http://localhost:8000/api/dcim/interfaces/47/", "device": { "id": 4, "url": "http://localhost:8000/api/dcim/devices/4/", "name": "test1-leaf1", "display_name": "test1-leaf1" }, "name": "et-0/0/49", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 152, "url": "http://localhost:8000/api/dcim/interfaces/152/", "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 5, "interface_a": { "id": 46, "url": "http://localhost:8000/api/dcim/interfaces/46/", "device": { "id": 4, "url": "http://localhost:8000/api/dcim/devices/4/", "name": "test1-leaf1", "display_name": "test1-leaf1" }, "name": "et-0/0/48", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 14, "url": "http://localhost:8000/api/dcim/interfaces/14/", "device": { "id": 3, "url": "http://localhost:8000/api/dcim/devices/3/", "name": "test1-spine1", "display_name": "test1-spine1" }, "name": "et-0/0/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 4, "interface_a": { "id": 100, "url": "http://localhost:8000/api/dcim/interfaces/100/", "device": { "id": 5, "url": "http://localhost:8000/api/dcim/devices/5/", "name": "test1-leaf2", "display_name": "test1-leaf2" }, "name": "et-0/0/49", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 153, "url": "http://localhost:8000/api/dcim/interfaces/153/", "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/1", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 3, "interface_a": { "id": 99, "url": "http://localhost:8000/api/dcim/interfaces/99/", "device": { "id": 5, "url": "http://localhost:8000/api/dcim/devices/5/", "name": "test1-leaf2", "display_name": "test1-leaf2" }, "name": "et-0/0/48", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 15, "url": "http://localhost:8000/api/dcim/interfaces/15/", "device": { "id": 3, "url": "http://localhost:8000/api/dcim/devices/3/", "name": "test1-spine1", "display_name": "test1-spine1" }, "name": "et-0/0/1", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 25, "interface_a": { "id": 217, "url": "http://localhost:8000/api/dcim/interfaces/217/", "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/4", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 205, "url": "http://localhost:8000/api/dcim/interfaces/205/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/4", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 26, "interface_a": { "id": 216, "url": "http://localhost:8000/api/dcim/interfaces/216/", "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/3", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 211, "url": "http://localhost:8000/api/dcim/interfaces/211/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/4", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 19, "interface_a": { "id": 198, "url": "http://localhost:8000/api/dcim/interfaces/198/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/1/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 179, "url": "http://localhost:8000/api/dcim/interfaces/179/", "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/2/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } }, { "id": 18, "interface_a": { "id": 195, "url": "http://localhost:8000/api/dcim/interfaces/195/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/0/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "interface_b": { "id": 41, "url": "http://localhost:8000/api/dcim/interfaces/41/", "device": { "id": 3, "url": "http://localhost:8000/api/dcim/devices/3/", "name": "test1-spine1", "display_name": "test1-spine1" }, "name": "et-0/2/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" }, "connection_status": { "value": true, "label": "Connected" } } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/interface_template.json0000644000175100017510000000076415136437134023536 0ustar00runnerrunner{ "id": 1, "device_type": { "id": 1, "url": "http://localhost:8000/api/dcim/device-types/1/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960" }, "name": "fxp0 (RE0)", "form_factor": { "value": 1000, "label": "1000BASE-T (1GE)" }, "mgmt_only": true }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/interface_templates.json0000644000175100017510000000262115136437134023713 0ustar00runnerrunner{ "count": 2, "next": null, "previous": null, "results": [ { "id": 4, "device_type": { "id": 2, "url": "http://localhost:8000/api/dcim/device-types/2/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "EX9214", "slug": "ex9214" }, "name": "fxp0 (RE0)", "form_factor": { "value": 1000, "label": "1000BASE-T (1GE)" }, "mgmt_only": true }, { "id": 5, "device_type": { "id": 2, "url": "http://localhost:8000/api/dcim/device-types/2/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "EX9214", "slug": "ex9214" }, "name": "fxp0 (RE1)", "form_factor": { "value": 1000, "label": "1000BASE-T (1GE)" }, "mgmt_only": true } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/interface_trace.json0000644000175100017510000000677015136437134023024 0ustar00runnerrunner[ [ { "id": 39126, "url": "http://localhost:8000/api/dcim/interfaces/39126/", "device": { "id": 4747, "url": "http://localhost:8000/api/dcim/devices/4747/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "em1", "cable": 9911, "connection_status": { "value": false, "label": "Not Connected" } }, { "id": 9911, "url": "http://localhost:8000/api/dcim/cables/9911/", "type": "", "status": "planned", "label": "", "color": "", "length": null, "length_unit": "" }, { "id": 5583, "url": "http://localhost:8000/api/dcim/front-ports/5583/", "device": { "id": 4430, "url": "http://localhost:8000/api/dcim/devices/4430/", "name": "test1-patchpanel1", "display_name": "test1-patchpanel1" }, "name": "pair-11 (ports 21-22)", "cable": 9911 } ], [ { "id": 3736, "url": "http://localhost:8000/api/dcim/rear-ports/3736/", "device": { "id": 4430, "url": "http://localhost:8000/api/dcim/devices/4430/", "name": "test1-patchpanel1", "display_name": "test1-patchpanel1" }, "name": "port-2", "cable": 9229 }, { "id": 9229, "url": "http://localhost:8000/api/dcim/cables/9229/", "type": "mmf-om4", "status": "planned", "label": "", "color": "", "length": null, "length_unit": "" }, { "id": 3768, "url": "http://localhost:8000/api/dcim/rear-ports/3768/", "device": { "id": 4436, "url": "http://localhost:8000/api/dcim/devices/4436/", "name": "test1-patchpanel2", "display_name": "test1-patchpanel2" }, "name": "port-2", "cable": 9229 } ], [ { "id": 5655, "url": "http://localhost:8000/api/dcim/front-ports/5655/", "device": { "id": 4436, "url": "http://localhost:8000/api/dcim/devices/4436/", "name": "test1-patchpanel2", "display_name": "test1-patchpanel2" }, "name": "pair-11 (ports 21-22)", "cable": 9240 }, { "id": 9240, "url": "http://localhost:8000/api/dcim/cables/9240/", "type": "mmf-om4", "status": "planned", "label": "", "color": "", "length": null, "length_unit": "" }, { "id": 35473, "url": "http://localhost:8000/api/dcim/interfaces/35473/", "device": { "id": 3930, "url": "http://localhost:8000/api/dcim/devices/3930/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "Ethernet11", "cable": 9240, "connection_status": { "value": false, "label": "Not Connected" } } ] ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/interfaces.json0000644000175100017510000003356715136437134022035 0ustar00runnerrunner{ "count": 221, "next": null, "previous": "http://localhost:8000/api/dcim/interfaces/?limit=50&offset=150", "results": [ { "id": 162, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/18", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 163, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/19", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 164, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/2", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 165, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/20", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 166, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/21", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 167, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/22", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 168, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/3", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 169, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/4", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 170, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/5", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 171, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/6", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 172, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/7", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 173, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/8", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 174, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/9", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 175, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/1/0", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 17, "url": "http://localhost:8000/api/dcim/interface-connections/17/", "connection_status": true }, "connected_interface": { "id": 192, "url": "http://localhost:8000/api/dcim/interfaces/192/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/1/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 176, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/1/1", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 177, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/1/2", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 178, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/1/3", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 179, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/2/0", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 19, "url": "http://localhost:8000/api/dcim/interface-connections/19/", "connection_status": true }, "connected_interface": { "id": 198, "url": "http://localhost:8000/api/dcim/interfaces/198/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/1/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 180, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/2/1", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 181, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/2/2", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 182, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/2/3", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/interfaces_1.json0000644000175100017510000012450715136437134022250 0ustar00runnerrunner{ "count": 221, "next": "http://localhost:8000/api/dcim/interfaces/?limit=50&offset=50", "previous": null, "results": [ { "id": 189, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/0/0", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 16, "url": "http://localhost:8000/api/dcim/interface-connections/16/", "connection_status": true }, "connected_interface": { "id": 37, "url": "http://localhost:8000/api/dcim/interfaces/37/", "device": { "id": 3, "url": "http://localhost:8000/api/dcim/devices/3/", "name": "test1-spine1", "display_name": "test1-spine1" }, "name": "et-0/1/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 190, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/0/1", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 191, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/0/2", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 20, "url": "http://localhost:8000/api/dcim/interface-connections/20/", "connection_status": true }, "connected_interface": { "id": 197, "url": "http://localhost:8000/api/dcim/interfaces/197/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/0/2", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 192, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/1/0", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 17, "url": "http://localhost:8000/api/dcim/interface-connections/17/", "connection_status": true }, "connected_interface": { "id": 175, "url": "http://localhost:8000/api/dcim/interfaces/175/", "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/1/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 193, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/1/1", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 194, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/1/2", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 21, "url": "http://localhost:8000/api/dcim/interface-connections/21/", "connection_status": true }, "connected_interface": { "id": 200, "url": "http://localhost:8000/api/dcim/interfaces/200/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/1/2", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 10, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "fxp0 (RE0)", "form_factor": { "value": 1000, "label": "1000BASE-T (1GE)" }, "lag": null, "mac_address": null, "mgmt_only": true, "description": "", "connection": null, "connected_interface": null }, { "id": 11, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "fxp0 (RE1)", "form_factor": { "value": 1000, "label": "1000BASE-T (1GE)" }, "lag": null, "mac_address": null, "mgmt_only": true, "description": "", "connection": null, "connected_interface": null }, { "id": 12, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "lo0", "form_factor": { "value": 0, "label": "Virtual" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 201, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/0", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 202, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/1", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 203, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/2", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 204, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/3", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 205, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/4", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 25, "url": "http://localhost:8000/api/dcim/interface-connections/25/", "connection_status": true }, "connected_interface": { "id": 217, "url": "http://localhost:8000/api/dcim/interfaces/217/", "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/4", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 206, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/5", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 23, "url": "http://localhost:8000/api/dcim/interface-connections/23/", "connection_status": true }, "connected_interface": { "id": 8, "url": "http://localhost:8000/api/dcim/interfaces/8/", "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/4", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 195, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/0/0", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 18, "url": "http://localhost:8000/api/dcim/interface-connections/18/", "connection_status": true }, "connected_interface": { "id": 41, "url": "http://localhost:8000/api/dcim/interfaces/41/", "device": { "id": 3, "url": "http://localhost:8000/api/dcim/devices/3/", "name": "test1-spine1", "display_name": "test1-spine1" }, "name": "et-0/2/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 196, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/0/1", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 197, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/0/2", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 20, "url": "http://localhost:8000/api/dcim/interface-connections/20/", "connection_status": true }, "connected_interface": { "id": 191, "url": "http://localhost:8000/api/dcim/interfaces/191/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/0/2", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 198, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/1/0", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 19, "url": "http://localhost:8000/api/dcim/interface-connections/19/", "connection_status": true }, "connected_interface": { "id": 179, "url": "http://localhost:8000/api/dcim/interfaces/179/", "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/2/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 199, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/1/1", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 200, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/1/2", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 21, "url": "http://localhost:8000/api/dcim/interface-connections/21/", "connection_status": true }, "connected_interface": { "id": 194, "url": "http://localhost:8000/api/dcim/interfaces/194/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/1/2", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 186, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "fxp0 (RE0)", "form_factor": { "value": 1000, "label": "1000BASE-T (1GE)" }, "lag": null, "mac_address": null, "mgmt_only": true, "description": "", "connection": null, "connected_interface": null }, { "id": 187, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "fxp0 (RE1)", "form_factor": { "value": 1000, "label": "1000BASE-T (1GE)" }, "lag": null, "mac_address": null, "mgmt_only": true, "description": "", "connection": null, "connected_interface": null }, { "id": 188, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "lo0", "form_factor": { "value": 0, "label": "Virtual" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 207, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/0", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 208, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/1", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 209, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/2", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 210, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/3", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 211, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/4", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 26, "url": "http://localhost:8000/api/dcim/interface-connections/26/", "connection_status": true }, "connected_interface": { "id": 216, "url": "http://localhost:8000/api/dcim/interfaces/216/", "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/3", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 212, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/5", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 24, "url": "http://localhost:8000/api/dcim/interface-connections/24/", "connection_status": true }, "connected_interface": { "id": 7, "url": "http://localhost:8000/api/dcim/interfaces/7/", "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/3", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 1, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "fxp0 (RE0)", "form_factor": { "value": 1000, "label": "1000BASE-T (1GE)" }, "lag": null, "mac_address": null, "mgmt_only": true, "description": "", "connection": null, "connected_interface": null }, { "id": 2, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "fxp0 (RE1)", "form_factor": { "value": 800, "label": "100BASE-TX (10/100ME)" }, "lag": null, "mac_address": null, "mgmt_only": true, "description": "", "connection": null, "connected_interface": null }, { "id": 3, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "lo0", "form_factor": { "value": 0, "label": "Virtual" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 4, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/0", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "TEST", "connection": null, "connected_interface": null }, { "id": 5, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/1", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 6, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/2", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 7, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/3", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 24, "url": "http://localhost:8000/api/dcim/interface-connections/24/", "connection_status": true }, "connected_interface": { "id": 212, "url": "http://localhost:8000/api/dcim/interfaces/212/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/5", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 8, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/4", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 23, "url": "http://localhost:8000/api/dcim/interface-connections/23/", "connection_status": true }, "connected_interface": { "id": 206, "url": "http://localhost:8000/api/dcim/interfaces/206/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/5", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 9, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/5", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 22, "url": "http://localhost:8000/api/dcim/interface-connections/22/", "connection_status": true }, "connected_interface": { "id": 218, "url": "http://localhost:8000/api/dcim/interfaces/218/", "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/5", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 183, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "fxp0 (RE0)", "form_factor": { "value": 1000, "label": "1000BASE-T (1GE)" }, "lag": null, "mac_address": null, "mgmt_only": true, "description": "", "connection": null, "connected_interface": null }, { "id": 184, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "fxp0 (RE1)", "form_factor": { "value": 800, "label": "100BASE-TX (10/100ME)" }, "lag": null, "mac_address": null, "mgmt_only": true, "description": "", "connection": null, "connected_interface": null }, { "id": 185, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "lo0", "form_factor": { "value": 0, "label": "Virtual" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 213, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/0", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 214, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/1", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 215, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/2", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 216, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/3", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 26, "url": "http://localhost:8000/api/dcim/interface-connections/26/", "connection_status": true }, "connected_interface": { "id": 211, "url": "http://localhost:8000/api/dcim/interfaces/211/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/4", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 217, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/4", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 25, "url": "http://localhost:8000/api/dcim/interface-connections/25/", "connection_status": true }, "connected_interface": { "id": 205, "url": "http://localhost:8000/api/dcim/interfaces/205/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/4", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 218, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/5", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 22, "url": "http://localhost:8000/api/dcim/interface-connections/22/", "connection_status": true }, "connected_interface": { "id": 9, "url": "http://localhost:8000/api/dcim/interfaces/9/", "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/5", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 45, "device": { "id": 4, "url": "http://localhost:8000/api/dcim/devices/4/", "name": "test1-leaf1", "display_name": "test1-leaf1" }, "name": "em0", "form_factor": { "value": 1000, "label": "1000BASE-T (1GE)" }, "lag": null, "mac_address": "FF:EE:DD:33:22:11", "mgmt_only": true, "description": "", "connection": null, "connected_interface": null }, { "id": 46, "device": { "id": 4, "url": "http://localhost:8000/api/dcim/devices/4/", "name": "test1-leaf1", "display_name": "test1-leaf1" }, "name": "et-0/0/48", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 5, "url": "http://localhost:8000/api/dcim/interface-connections/5/", "connection_status": true }, "connected_interface": { "id": 14, "url": "http://localhost:8000/api/dcim/interfaces/14/", "device": { "id": 3, "url": "http://localhost:8000/api/dcim/devices/3/", "name": "test1-spine1", "display_name": "test1-spine1" }, "name": "et-0/0/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/interfaces_2.json0000644000175100017510000003356715136437134022256 0ustar00runnerrunner{ "count": 221, "next": null, "previous": "http://localhost:8000/api/dcim/interfaces/?limit=50&offset=150", "results": [ { "id": 162, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/18", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 163, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/19", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 164, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/2", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 165, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/20", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 166, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/21", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 167, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/22", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 168, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/3", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 169, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/4", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 170, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/5", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 171, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/6", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 172, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/7", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 173, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/8", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 174, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/0/9", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 175, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/1/0", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 17, "url": "http://localhost:8000/api/dcim/interface-connections/17/", "connection_status": true }, "connected_interface": { "id": 192, "url": "http://localhost:8000/api/dcim/interfaces/192/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/1/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 176, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/1/1", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 177, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/1/2", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 178, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/1/3", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 179, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/2/0", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 19, "url": "http://localhost:8000/api/dcim/interface-connections/19/", "connection_status": true }, "connected_interface": { "id": 198, "url": "http://localhost:8000/api/dcim/interfaces/198/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/1/0", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, { "id": 180, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/2/1", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 181, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/2/2", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, { "id": 182, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "et-0/2/3", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/inventory_item.json0000644000175100017510000000003615136437134022746 0ustar00runnerrunner{ "detail": "Not found." }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/inventory_items.json0000644000175100017510000000011515136437134023127 0ustar00runnerrunner{ "count": 0, "next": null, "previous": null, "results": [] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/manufacturer.json0000644000175100017510000000007515136437134022372 0ustar00runnerrunner{ "id": 1, "name": "Juniper", "slug": "juniper" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/manufacturers.json0000644000175100017510000000061615136437134022556 0ustar00runnerrunner{ "count": 3, "next": null, "previous": null, "results": [ { "id": 1, "name": "Juniper", "slug": "juniper" }, { "id": 2, "name": "Opengear", "slug": "opengear" }, { "id": 3, "name": "ServerTech", "slug": "servertech" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/napalm.json0000644000175100017510000000044615136437134021150 0ustar00runnerrunner{ "get_facts": { "interface_list": [ "xe-0/0/0" ], "serial_number": "ABC!@#", "vendor": "PacketPusher", "os_version": "1.1", "hostname": "test-1", "model": "UnobtaniumX", "fqdn": "None", "uptime": 2 } }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/platform.json0000644000175100017510000000015415136437134021520 0ustar00runnerrunner{ "id": 1, "name": "Juniper Junos", "slug": "juniper-junos", "rpc_client": "juniper-junos" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/platforms.json0000644000175100017510000000057615136437134021713 0ustar00runnerrunner{ "count": 2, "next": null, "previous": null, "results": [ { "id": 1, "name": "Juniper Junos", "slug": "juniper-junos", "rpc_client": "juniper-junos" }, { "id": 2, "name": "Opengear", "slug": "opengear", "rpc_client": "opengear" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/power_outlet.json0000644000175100017510000000034415136437134022425 0ustar00runnerrunner{ "id": 1, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA1", "connected_port": 1 }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/power_outlet_template.json0000644000175100017510000000062715136437134024324 0ustar00runnerrunner{ "id": 1, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AA1" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/power_outlet_templates.json0000644000175100017510000003127415136437134024511 0ustar00runnerrunner{ "count": 24, "next": null, "previous": null, "results": [ { "id": 4, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AA1" }, { "id": 5, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AA2" }, { "id": 6, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AA3" }, { "id": 7, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AA4" }, { "id": 8, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AA5" }, { "id": 9, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AA6" }, { "id": 10, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AA7" }, { "id": 11, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AA8" }, { "id": 12, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AB1" }, { "id": 13, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AB2" }, { "id": 14, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AB3" }, { "id": 15, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AB4" }, { "id": 16, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AB5" }, { "id": 17, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AB6" }, { "id": 18, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AB7" }, { "id": 19, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AB8" }, { "id": 20, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AC1" }, { "id": 21, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AC2" }, { "id": 22, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AC3" }, { "id": 23, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AC4" }, { "id": 24, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AC5" }, { "id": 25, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AC6" }, { "id": 26, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AC7" }, { "id": 27, "device_type": { "id": 6, "url": "http://localhost:8000/api/dcim/device-types/6/", "manufacturer": { "id": 3, "url": "http://localhost:8000/api/dcim/manufacturers/3/", "name": "ServerTech", "slug": "servertech" }, "model": "CWG-24VYM415C9", "slug": "cwg-24vym415c9" }, "name": "AC8" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/power_outlets.json0000644000175100017510000003621515136437134022616 0ustar00runnerrunner{ "count": 48, "next": null, "previous": null, "results": [ { "id": 25, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA1", "connected_port": 1 }, { "id": 26, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA2", "connected_port": 5 }, { "id": 27, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA3", "connected_port": 13 }, { "id": 28, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA4", "connected_port": 9 }, { "id": 29, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA5", "connected_port": 16 }, { "id": 30, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA6", "connected_port": 20 }, { "id": 31, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA7", "connected_port": 24 }, { "id": 32, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA8", "connected_port": 12 }, { "id": 33, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AB1", "connected_port": null }, { "id": 34, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AB2", "connected_port": null }, { "id": 35, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AB3", "connected_port": null }, { "id": 36, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AB4", "connected_port": null }, { "id": 37, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AB5", "connected_port": null }, { "id": 38, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AB6", "connected_port": null }, { "id": 39, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AB7", "connected_port": null }, { "id": 40, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AB8", "connected_port": null }, { "id": 41, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AC1", "connected_port": null }, { "id": 42, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AC2", "connected_port": null }, { "id": 43, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AC3", "connected_port": null }, { "id": 44, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AC4", "connected_port": null }, { "id": 45, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AC5", "connected_port": null }, { "id": 46, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AC6", "connected_port": null }, { "id": 47, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AC7", "connected_port": null }, { "id": 48, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AC8", "connected_port": null }, { "id": 49, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA1", "connected_port": 2 }, { "id": 50, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA2", "connected_port": 6 }, { "id": 51, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA3", "connected_port": 14 }, { "id": 52, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA4", "connected_port": 10 }, { "id": 53, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA5", "connected_port": 15 }, { "id": 54, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA6", "connected_port": 19 }, { "id": 55, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA7", "connected_port": 23 }, { "id": 56, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA8", "connected_port": 11 }, { "id": 57, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AB1", "connected_port": null }, { "id": 58, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AB2", "connected_port": null }, { "id": 59, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AB3", "connected_port": null }, { "id": 60, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AB4", "connected_port": null }, { "id": 61, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AB5", "connected_port": null }, { "id": 62, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AB6", "connected_port": null }, { "id": 63, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AB7", "connected_port": null }, { "id": 64, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AB8", "connected_port": null }, { "id": 65, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AC1", "connected_port": null }, { "id": 66, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AC2", "connected_port": null }, { "id": 67, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AC3", "connected_port": null }, { "id": 68, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AC4", "connected_port": null }, { "id": 69, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AC5", "connected_port": null }, { "id": 70, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AC6", "connected_port": null }, { "id": 71, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AC7", "connected_port": null }, { "id": 72, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AC8", "connected_port": null } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/power_port.json0000644000175100017510000000101615136437134022072 0ustar00runnerrunner{ "id": 1, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "PEM0", "power_outlet": { "id": 25, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA1", "connected_port": 1 }, "connection_status": true }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/power_port_template.json0000644000175100017510000000060015136437134023763 0ustar00runnerrunner{ "id": 1, "device_type": { "id": 1, "url": "http://localhost:8000/api/dcim/device-types/1/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960" }, "name": "PEM0" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/power_port_templates.json0000644000175100017510000001525315136437134024160 0ustar00runnerrunner{ "count": 13, "next": null, "previous": null, "results": [ { "id": 5, "device_type": { "id": 2, "url": "http://localhost:8000/api/dcim/device-types/2/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "EX9214", "slug": "ex9214" }, "name": "PEM0" }, { "id": 6, "device_type": { "id": 2, "url": "http://localhost:8000/api/dcim/device-types/2/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "EX9214", "slug": "ex9214" }, "name": "PEM1" }, { "id": 7, "device_type": { "id": 2, "url": "http://localhost:8000/api/dcim/device-types/2/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "EX9214", "slug": "ex9214" }, "name": "PEM2" }, { "id": 8, "device_type": { "id": 2, "url": "http://localhost:8000/api/dcim/device-types/2/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "EX9214", "slug": "ex9214" }, "name": "PEM3" }, { "id": 1, "device_type": { "id": 1, "url": "http://localhost:8000/api/dcim/device-types/1/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960" }, "name": "PEM0" }, { "id": 2, "device_type": { "id": 1, "url": "http://localhost:8000/api/dcim/device-types/1/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960" }, "name": "PEM1" }, { "id": 3, "device_type": { "id": 1, "url": "http://localhost:8000/api/dcim/device-types/1/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960" }, "name": "PEM2" }, { "id": 4, "device_type": { "id": 1, "url": "http://localhost:8000/api/dcim/device-types/1/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "MX960", "slug": "mx960" }, "name": "PEM3" }, { "id": 11, "device_type": { "id": 3, "url": "http://localhost:8000/api/dcim/device-types/3/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-24Q", "slug": "qfx5100-24q" }, "name": "PSU0" }, { "id": 12, "device_type": { "id": 3, "url": "http://localhost:8000/api/dcim/device-types/3/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-24Q", "slug": "qfx5100-24q" }, "name": "PSU1" }, { "id": 9, "device_type": { "id": 4, "url": "http://localhost:8000/api/dcim/device-types/4/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-48S", "slug": "qfx5100-48s" }, "name": "PSU0" }, { "id": 13, "device_type": { "id": 4, "url": "http://localhost:8000/api/dcim/device-types/4/", "manufacturer": { "id": 1, "url": "http://localhost:8000/api/dcim/manufacturers/1/", "name": "Juniper", "slug": "juniper" }, "model": "QFX5100-48S", "slug": "qfx5100-48s" }, "name": "PSU1" }, { "id": 14, "device_type": { "id": 5, "url": "http://localhost:8000/api/dcim/device-types/5/", "manufacturer": { "id": 2, "url": "http://localhost:8000/api/dcim/manufacturers/2/", "name": "Opengear", "slug": "opengear" }, "model": "CM4148", "slug": "cm4148" }, "name": "PSU" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/power_ports.json0000644000175100017510000003442115136437134022263 0ustar00runnerrunner{ "count": 25, "next": null, "previous": null, "results": [ { "id": 5, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "PEM0", "power_outlet": { "id": 26, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA2", "connected_port": 5 }, "connection_status": true }, { "id": 6, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "PEM1", "power_outlet": { "id": 50, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA2", "connected_port": 6 }, "connection_status": true }, { "id": 7, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "PEM2", "power_outlet": null, "connection_status": true }, { "id": 8, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "PEM3", "power_outlet": null, "connection_status": true }, { "id": 19, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "PEM0", "power_outlet": { "id": 54, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA6", "connected_port": 19 }, "connection_status": true }, { "id": 20, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "PEM1", "power_outlet": { "id": 30, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA6", "connected_port": 20 }, "connection_status": true }, { "id": 21, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "PEM2", "power_outlet": null, "connection_status": true }, { "id": 22, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "PEM3", "power_outlet": null, "connection_status": true }, { "id": 1, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "PEM0", "power_outlet": { "id": 25, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA1", "connected_port": 1 }, "connection_status": true }, { "id": 2, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "PEM1", "power_outlet": { "id": 49, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA1", "connected_port": 2 }, "connection_status": true }, { "id": 3, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "PEM2", "power_outlet": null, "connection_status": true }, { "id": 4, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "PEM3", "power_outlet": null, "connection_status": true }, { "id": 15, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "PEM0", "power_outlet": { "id": 53, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA5", "connected_port": 15 }, "connection_status": true }, { "id": 16, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "PEM1", "power_outlet": { "id": 29, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA5", "connected_port": 16 }, "connection_status": true }, { "id": 17, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "PEM2", "power_outlet": null, "connection_status": true }, { "id": 18, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "PEM3", "power_outlet": null, "connection_status": true }, { "id": 9, "device": { "id": 4, "url": "http://localhost:8000/api/dcim/devices/4/", "name": "test1-leaf1", "display_name": "test1-leaf1" }, "name": "PSU0", "power_outlet": { "id": 28, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA4", "connected_port": 9 }, "connection_status": true }, { "id": 10, "device": { "id": 4, "url": "http://localhost:8000/api/dcim/devices/4/", "name": "test1-leaf1", "display_name": "test1-leaf1" }, "name": "PSU1", "power_outlet": { "id": 52, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA4", "connected_port": 10 }, "connection_status": true }, { "id": 11, "device": { "id": 5, "url": "http://localhost:8000/api/dcim/devices/5/", "name": "test1-leaf2", "display_name": "test1-leaf2" }, "name": "PSU0", "power_outlet": { "id": 56, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA8", "connected_port": 11 }, "connection_status": true }, { "id": 12, "device": { "id": 5, "url": "http://localhost:8000/api/dcim/devices/5/", "name": "test1-leaf2", "display_name": "test1-leaf2" }, "name": "PSU1", "power_outlet": { "id": 32, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA8", "connected_port": 12 }, "connection_status": true }, { "id": 25, "device": { "id": 9, "url": "http://localhost:8000/api/dcim/devices/9/", "name": "test1-oob1", "display_name": "test1-oob1" }, "name": "PSU", "power_outlet": null, "connection_status": true }, { "id": 13, "device": { "id": 3, "url": "http://localhost:8000/api/dcim/devices/3/", "name": "test1-spine1", "display_name": "test1-spine1" }, "name": "PSU0", "power_outlet": { "id": 27, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA3", "connected_port": 13 }, "connection_status": true }, { "id": 14, "device": { "id": 3, "url": "http://localhost:8000/api/dcim/devices/3/", "name": "test1-spine1", "display_name": "test1-spine1" }, "name": "PSU1", "power_outlet": { "id": 51, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA3", "connected_port": 14 }, "connection_status": true }, { "id": 23, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "PSU0", "power_outlet": { "id": 55, "device": { "id": 12, "url": "http://localhost:8000/api/dcim/devices/12/", "name": "test1-pdu2", "display_name": "test1-pdu2" }, "name": "AA7", "connected_port": 23 }, "connection_status": true }, { "id": 24, "device": { "id": 6, "url": "http://localhost:8000/api/dcim/devices/6/", "name": "test1-spine2", "display_name": "test1-spine2" }, "name": "PSU1", "power_outlet": { "id": 31, "device": { "id": 11, "url": "http://localhost:8000/api/dcim/devices/11/", "name": "test1-pdu1", "display_name": "test1-pdu1" }, "name": "AA7", "connected_port": 24 }, "connection_status": true } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/rack.json0000644000175100017510000000115315136437134020614 0ustar00runnerrunner{ "id": 1, "name": "A1R1", "facility_id": "T23A01", "display_name": "A1R1 (T23A01)", "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "group": null, "tenant": null, "role": { "id": 1, "url": "http://localhost:8000/api/dcim/rack-roles/1/", "name": "Compute", "slug": "compute" }, "type": null, "width": { "value": 19, "label": "19 inches" }, "u_height": 42, "desc_units": false, "comments": "", "custom_fields": {} }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/rack_group.json0000644000175100017510000000031015136437134022022 0ustar00runnerrunner{ "id": 1, "name": "TEST", "slug": "test", "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" } }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/rack_groups.json0000644000175100017510000000056315136437134022217 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "TEST", "slug": "test", "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" } } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/rack_reservation.json0000644000175100017510000000046415136437134023241 0ustar00runnerrunner{ "id": 1, "rack": { "id": 2, "url": "http://localhost:8000/api/dcim/racks/2/", "name": "A1R2", "display_name": "A1R2 (T24A01)" }, "units": [ 42 ], "created": "2017-03-22T04:10:47.307156Z", "user": 1, "description": "Test Reservation" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/rack_reservations.json0000644000175100017510000000077715136437134023433 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "rack": { "id": 2, "url": "http://localhost:8000/api/dcim/racks/2/", "name": "A1R2", "display_name": "A1R2 (T24A01)" }, "units": [ 42 ], "created": "2017-03-22T04:10:47.307156Z", "user": 1, "description": "Test Reservation" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/rack_role.json0000644000175100017510000000011615136437134021633 0ustar00runnerrunner{ "id": 1, "name": "Test", "slug": "test", "color": "aa1409" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/rack_roles.json0000644000175100017510000000032115136437134022014 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "Test", "slug": "test", "color": "aa1409" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/rack_u.json0000644000175100017510000000146515136437134021146 0ustar00runnerrunner{ "count": 3, "next": null, "previous": null, "results": [ { "id": 48, "name": "U1", "face": 0, "device": { "id": 130, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "tst-device1", "display_name": "tst-device1" } }, { "id": 47, "name": "U2", "face": 0, "device": null }, { "id": 46, "name": "U3", "face": 0, "device": { "id": 1859, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "tst-device2", "display_name": "tst-device2" } } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/racks.json0000644000175100017510000000263315136437134021003 0ustar00runnerrunner{ "count": 2, "next": null, "previous": null, "results": [ { "id": 1, "name": "A1R1", "facility_id": "T23A01", "display_name": "A1R1 (T23A01)", "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "group": null, "tenant": null, "role": null, "type": null, "width": { "value": 19, "label": "19 inches" }, "u_height": 42, "desc_units": false, "comments": "", "custom_fields": {} }, { "id": 2, "name": "A1R2", "facility_id": "T24A01", "display_name": "A1R2 (T24A01)", "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "group": null, "tenant": null, "role": null, "type": null, "width": { "value": 19, "label": "19 inches" }, "u_height": 42, "desc_units": false, "comments": "", "custom_fields": {} } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/region.json0000644000175100017510000000011315136437134021152 0ustar00runnerrunner{ "id": 1, "name": "TEST", "slug": "test", "parent": null }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/regions.json0000644000175100017510000000031615136437134021342 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "TEST", "slug": "test", "parent": null } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/site.json0000644000175100017510000000113215136437134020635 0ustar00runnerrunner{ "id": 1, "name": "TEST1", "slug": "test1", "region": null, "tenant": null, "facility": "Test Facility", "asn": 65535, "physical_address": "555 Test Ave.\r\nTest, NY 55555", "shipping_address": "", "contact_name": "", "contact_phone": "", "contact_email": "", "comments": "", "custom_fields": { "test_custom": "Hello", "test_selection": { "value": 2, "label": "second" } }, "count_prefixes": 2, "count_vlans": 1, "count_racks": 2, "count_devices": 11, "count_circuits": 0 }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/sites.json0000644000175100017510000000141015136437134021017 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "TEST1", "slug": "test1", "region": null, "tenant": null, "facility": "Test Facility", "asn": 65535, "physical_address": "555 Test Ave.\r\nTest, NY 55555", "shipping_address": "", "contact_name": "", "contact_phone": "", "contact_email": "", "comments": "", "custom_fields": { "test_custom": "Hello" }, "count_prefixes": 2, "count_vlans": 1, "count_racks": 2, "count_devices": 11, "count_circuits": 0 } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/virtual_chassis_device.json0000644000175100017510000000036615136437134024423 0ustar00runnerrunner{ "id": 1, "master": { "id": 5654, "url": "http://localhost:8000/api/dcim/devices/5654/", "name": "01-0001-e214-as01 (SW1)", "display_name": "01-0001-e214-as01 (SW1)" }, "domain": "test-domain-1" } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/dcim/virtual_chassis_devices.json0000644000175100017510000000136315136437134024604 0ustar00runnerrunner{ "count": 2, "next": null, "previous": null, "results": [ { "id": 1, "master": { "id": 5654, "url": "http://localhost:8000/api/dcim/devices/5654/", "name": "01-0001-e214-as01 (SW1)", "display_name": "01-0001-e214-as01 (SW1)" }, "domain": "test-domain-1" }, { "id": 2, "master": { "id": 5635, "url": "http://localhost:8000/netbox/api/dcim/devices/5635/", "name": "hercules.router.com (VSS-SW1)", "display_name": "hercules.router.com (VSS-SW1)" }, "domain": "test-domain-2" } ] } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8163304 pynetbox-7.6.1/tests/fixtures/ipam/0000755000175100017510000000000015136437144017014 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/aggregate.json0000644000175100017510000000042715136437134021637 0ustar00runnerrunner{ "id": 1, "family": 4, "prefix": "10.0.0.0/8", "rir": { "id": 1, "url": "http://localhost:8000/api/ipam/rirs/1/", "name": "RFC1918", "slug": "rfc1918" }, "date_added": null, "description": "", "custom_fields": {} }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/aggregates.json0000644000175100017510000000073215136437134022021 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "family": 4, "prefix": "10.0.0.0/8", "rir": { "id": 1, "url": "http://localhost:8000/api/ipam/rirs/1/", "name": "RFC1918", "slug": "rfc1918" }, "date_added": null, "description": "", "custom_fields": {} } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/available-ips-post.json0000644000175100017510000000027315136437134023404 0ustar00runnerrunner{ "id": 1, "address": "10.1.1.1/32", "vrf": null, "tenant": null, "status": 1, "role": null, "interface": null, "description": "", "nat_inside": null }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/available-ips.json0000644000175100017510000000041215136437134022414 0ustar00runnerrunner[ { "family": 4, "address": "10.1.1.2/27", "vrf": null }, { "family": 4, "address": "10.1.1.3/27", "vrf": null }, { "family": 4, "address": "10.1.1.7/27", "vrf": null } ]././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/available-prefixes-post.json0000644000175100017510000000037115136437134024435 0ustar00runnerrunner[ { "id": 4, "prefix": "10.1.1.0/30", "site": null, "vrf": null, "tenant": null, "vlan": null, "status": 1, "role": null, "is_pool": false, "description": "" } ]././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/available-prefixes.json0000644000175100017510000000013115136437134023444 0ustar00runnerrunner[ { "family": 4, "prefix": "10.1.1.0/24", "vrf": null } ]././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/ip_address.json0000644000175100017510000000142215136437134022022 0ustar00runnerrunner{ "id": 1, "family": 4, "address": "10.0.255.1/32", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 3, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "lo0", "form_factor": { "value": 0, "label": "Virtual" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/ip_addresses.json0000644000175100017510000007003615136437134022361 0ustar00runnerrunner{ "count": 18, "next": null, "previous": null, "results": [ { "id": 5, "family": 4, "address": "10.0.254.1/24", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 12, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "lo0", "form_factor": { "value": 0, "label": "Virtual" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 19, "family": 4, "address": "10.0.254.2/32", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 188, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "lo0", "form_factor": { "value": 0, "label": "Virtual" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 1, "family": 4, "address": "10.0.255.1/32", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 3, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "lo0", "form_factor": { "value": 0, "label": "Virtual" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 3, "family": 4, "address": "10.0.255.2/32", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 185, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "lo0", "form_factor": { "value": 0, "label": "Virtual" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 11, "family": 4, "address": "10.15.20.1/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 7, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/3", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 24, "url": "http://localhost:8000/api/dcim/interface-connections/24/", "connection_status": true }, "connected_interface": { "id": 212, "url": "http://localhost:8000/api/dcim/interfaces/212/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/5", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 8, "family": 4, "address": "10.15.21.1/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 218, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/5", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 22, "url": "http://localhost:8000/api/dcim/interface-connections/22/", "connection_status": true }, "connected_interface": { "id": 9, "url": "http://localhost:8000/api/dcim/interfaces/9/", "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/5", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 9, "family": 4, "address": "10.15.21.2/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 9, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/5", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 22, "url": "http://localhost:8000/api/dcim/interface-connections/22/", "connection_status": true }, "connected_interface": { "id": 218, "url": "http://localhost:8000/api/dcim/interfaces/218/", "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/5", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 10, "family": 4, "address": "10.15.22.1/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 8, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/4", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 23, "url": "http://localhost:8000/api/dcim/interface-connections/23/", "connection_status": true }, "connected_interface": { "id": 206, "url": "http://localhost:8000/api/dcim/interfaces/206/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/5", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 13, "family": 4, "address": "10.15.22.2/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 206, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/5", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 23, "url": "http://localhost:8000/api/dcim/interface-connections/23/", "connection_status": true }, "connected_interface": { "id": 8, "url": "http://localhost:8000/api/dcim/interfaces/8/", "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/4", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 17, "family": 4, "address": "10.15.22.2/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 212, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/5", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 24, "url": "http://localhost:8000/api/dcim/interface-connections/24/", "connection_status": true }, "connected_interface": { "id": 7, "url": "http://localhost:8000/api/dcim/interfaces/7/", "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/3", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 12, "family": 4, "address": "10.16.20.1/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 216, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/3", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 26, "url": "http://localhost:8000/api/dcim/interface-connections/26/", "connection_status": true }, "connected_interface": { "id": 211, "url": "http://localhost:8000/api/dcim/interfaces/211/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/4", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 16, "family": 4, "address": "10.16.20.2/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 211, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "xe-0/0/4", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 26, "url": "http://localhost:8000/api/dcim/interface-connections/26/", "connection_status": true }, "connected_interface": { "id": 216, "url": "http://localhost:8000/api/dcim/interfaces/216/", "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/3", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 14, "family": 4, "address": "10.16.22.1/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 217, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/4", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 25, "url": "http://localhost:8000/api/dcim/interface-connections/25/", "connection_status": true }, "connected_interface": { "id": 205, "url": "http://localhost:8000/api/dcim/interfaces/205/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/4", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 15, "family": 4, "address": "10.16.22.2/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 205, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "xe-0/0/4", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 25, "url": "http://localhost:8000/api/dcim/interface-connections/25/", "connection_status": true }, "connected_interface": { "id": 217, "url": "http://localhost:8000/api/dcim/interfaces/217/", "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/4", "form_factor": 1200, "mac_address": null, "mgmt_only": false, "description": "" } }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 4, "family": 4, "address": "169.254.1.1/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 213, "device": { "id": 7, "url": "http://localhost:8000/api/dcim/devices/7/", "name": "test1-edge2", "display_name": "test1-edge2" }, "name": "xe-0/0/0", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": null, "connected_interface": null }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 20, "family": 4, "address": "169.254.1.1/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 200, "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/1/2", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 21, "url": "http://localhost:8000/api/dcim/interface-connections/21/", "connection_status": true }, "connected_interface": { "id": 194, "url": "http://localhost:8000/api/dcim/interfaces/194/", "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/1/2", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 21, "family": 4, "address": "169.254.1.2/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 194, "device": { "id": 2, "url": "http://localhost:8000/api/dcim/devices/2/", "name": "test1-core1", "display_name": "test1-core1" }, "name": "et-0/1/2", "form_factor": { "value": 1400, "label": "QSFP+ (40GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "", "connection": { "id": 21, "url": "http://localhost:8000/api/dcim/interface-connections/21/", "connection_status": true }, "connected_interface": { "id": 200, "url": "http://localhost:8000/api/dcim/interfaces/200/", "device": { "id": 8, "url": "http://localhost:8000/api/dcim/devices/8/", "name": "test1-core2", "display_name": "test1-core2" }, "name": "et-0/1/2", "form_factor": 1400, "mac_address": null, "mgmt_only": false, "description": "" } }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} }, { "id": 2, "family": 4, "address": "169.254.254.1/31", "vrf": null, "tenant": null, "status": { "value": 1, "label": "Active" }, "interface": { "id": 4, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "test1-edge1", "display_name": "test1-edge1" }, "name": "xe-0/0/0", "form_factor": { "value": 1200, "label": "SFP+ (10GE)" }, "lag": null, "mac_address": null, "mgmt_only": false, "description": "TEST", "connection": null, "connected_interface": null }, "description": "", "nat_inside": null, "nat_outside": null, "custom_fields": {} } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/prefix.json0000644000175100017510000000105515136437134021204 0ustar00runnerrunner{ "id": 1, "family": 4, "prefix": "10.1.1.0/24", "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "vrf": null, "tenant": null, "vlan": null, "status": { "value": 1, "label": "Active" }, "role": { "id": 1, "url": "http://localhost:8000/api/ipam/roles/1/", "name": "Lab Network", "slug": "lab-network" }, "is_pool": false, "description": "", "custom_fields": {} }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/prefixes.json0000644000175100017510000000314115136437134021532 0ustar00runnerrunner{ "count": 2, "next": null, "previous": null, "results": [ { "id": 2, "family": 4, "prefix": "10.0.255.0/24", "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "vrf": null, "tenant": null, "vlan": null, "status": { "value": 1, "label": "Active" }, "role": { "id": 1, "url": "http://localhost:8000/api/ipam/roles/1/", "name": "Lab Network", "slug": "lab-network" }, "is_pool": false, "description": "", "custom_fields": {} }, { "id": 1, "family": 4, "prefix": "10.1.1.0/24", "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "vrf": null, "tenant": null, "vlan": null, "status": { "value": 1, "label": "Active" }, "role": { "id": 1, "url": "http://localhost:8000/api/ipam/roles/1/", "name": "Lab Network", "slug": "lab-network" }, "is_pool": false, "description": "", "custom_fields": {} } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/rir.json0000644000175100017510000000012615136437134020501 0ustar00runnerrunner{ "id": 1, "name": "RFC1918", "slug": "rfc1918", "is_private": false }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/rirs.json0000644000175100017510000000033115136437134020662 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "RFC1918", "slug": "rfc1918", "is_private": false } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/role.json0000644000175100017510000000013115136437134020642 0ustar00runnerrunner{ "id": 1, "name": "Lab Network", "slug": "lab-network", "weight": 1000 }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/roles.json0000644000175100017510000000033415136437134021032 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "Lab Network", "slug": "lab-network", "weight": 1000 } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/vlan.json0000644000175100017510000000104315136437134020644 0ustar00runnerrunner{ "id": 3, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "group": null, "vid": 1210, "name": "v1210", "tenant": null, "status": { "value": 1, "label": "Active" }, "role": { "id": 1, "url": "http://localhost:8000/api/ipam/roles/1/", "name": "Lab Network", "slug": "lab-network" }, "description": "", "display_name": "1210 (v1210)", "custom_fields": {} }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/vlan_group.json0000644000175100017510000000031015136437134022054 0ustar00runnerrunner{ "id": 1, "name": "TEST", "slug": "test", "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" } }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/vlan_groups.json0000644000175100017510000000056315136437134022251 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "TEST", "slug": "test", "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" } } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/vlans.json0000644000175100017510000000445415136437134021040 0ustar00runnerrunner{ "count": 3, "next": null, "previous": null, "results": [ { "id": 1, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "group": null, "vid": 999, "name": "TEST", "tenant": null, "status": { "value": 1, "label": "Active" }, "role": { "id": 1, "url": "http://localhost:8000/api/ipam/roles/1/", "name": "Lab Network", "slug": "lab-network" }, "description": "", "display_name": "999 (TEST)", "custom_fields": {} }, { "id": 2, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "group": null, "vid": 2069, "name": "v2069", "tenant": null, "status": { "value": 1, "label": "Active" }, "role": { "id": 1, "url": "http://localhost:8000/api/ipam/roles/1/", "name": "Lab Network", "slug": "lab-network" }, "description": "", "display_name": "2069 (v2069)", "custom_fields": {} }, { "id": 3, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "group": null, "vid": 1210, "name": "v1210", "tenant": null, "status": { "value": 1, "label": "Active" }, "role": { "id": 1, "url": "http://localhost:8000/api/ipam/roles/1/", "name": "Lab Network", "slug": "lab-network" }, "description": "", "display_name": "1210 (v1210)", "custom_fields": {} } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/vrf.json0000644000175100017510000000023015136437134020476 0ustar00runnerrunner{ "id": 1, "name": "TEST", "rd": "65535:1", "tenant": null, "enforce_unique": true, "description": "", "custom_fields": {} }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/ipam/vrfs.json0000644000175100017510000000046315136437134020671 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "TEST", "rd": "65535:1", "tenant": null, "enforce_unique": true, "description": "", "custom_fields": {} } ] }././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8173304 pynetbox-7.6.1/tests/fixtures/tenancy/0000755000175100017510000000000015136437144017527 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/tenancy/tenant.json0000644000175100017510000000046415136437134021716 0ustar00runnerrunner{ "id": 1, "name": "TEST Tenant 1", "slug": "test-tenant-1", "group": { "id": 1, "url": "http://localhost:8000/api/tenancy/tenant-groups/1/", "name": "TEST Group", "slug": "test-group" }, "description": "", "comments": "", "custom_fields": {} }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/tenancy/tenant_group.json0000644000175100017510000000010315136437134023120 0ustar00runnerrunner{ "id": 1, "name": "TEST Group", "slug": "test-group" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/tenancy/tenant_groups.json0000644000175100017510000000027615136437134023316 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "TEST Group", "slug": "test-group" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/tenancy/tenants.json0000644000175100017510000000163515136437134022102 0ustar00runnerrunner{ "count": 2, "next": null, "previous": null, "results": [ { "id": 1, "name": "TEST Tenant 1", "slug": "test-tenant-1", "group": { "id": 1, "url": "http://localhost:8000/api/tenancy/tenant-groups/1/", "name": "TEST Group", "slug": "test-group" }, "description": "", "comments": "", "custom_fields": {} }, { "id": 2, "name": "TEST Tenant 2", "slug": "test-tenant-2", "group": { "id": 1, "url": "http://localhost:8000/api/tenancy/tenant-groups/1/", "name": "TEST Group", "slug": "test-group" }, "description": "", "comments": "", "custom_fields": {} } ] }././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8183303 pynetbox-7.6.1/tests/fixtures/users/0000755000175100017510000000000015136437144017227 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/users/group.json0000644000175100017510000000005215136437134021252 0ustar00runnerrunner{ "id": 1, "name": "usergroup1" } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/users/groups.json0000644000175100017510000000035015136437134021436 0ustar00runnerrunner{ "count": 2, "next": null, "previous": null, "results": [ { "id": 1, "name": "usergroup1" }, { "id": 2, "name": "usergroup2" } ] } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/users/permission.json0000644000175100017510000000037615136437134022317 0ustar00runnerrunner{ "id": 1, "name": "permission1", "users": [ { "username": "user1" } ], "constraints": [ { "status": "active" }, { "region__name": "Europe" } ] } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/users/permissions.json0000644000175100017510000000035215136437134022474 0ustar00runnerrunner{ "count": 2, "next": null, "previous": null, "results": [ { "id": 1, "name": "permission1" }, { "id": 2, "name": "permission2" } ] } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/users/unknown_model.json0000644000175100017510000000006115136437134022775 0ustar00runnerrunner{ "id": 1, "display": "Unknown object" } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/users/user.json0000644000175100017510000000005115136437134021073 0ustar00runnerrunner{ "id": 1, "username": "user1" } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/users/users.json0000644000175100017510000000034615136437134021265 0ustar00runnerrunner{ "count": 2, "next": null, "previous": null, "results": [ { "id": 1, "username": "user1" }, { "id": 2, "username": "user2" } ] } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8193305 pynetbox-7.6.1/tests/fixtures/virtualization/0000755000175100017510000000000015136437144021152 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/virtualization/cluster.json0000644000175100017510000000112215136437134023521 0ustar00runnerrunner{ "id": 1, "name": "vm-test-cluster", "type": { "id": 1, "url": "http://localhost:8000/api/virtualization/cluster-types/1/", "name": "vm-test-type", "slug": "vm-test-type" }, "group": { "id": 1, "url": "http://localhost:8000/api/virtualization/cluster-groups/1/", "name": "vm-test-group", "slug": "vm-test-group" }, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "comments": "", "custom_fields": {} }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/virtualization/cluster_group.json0000644000175100017510000000011115136437134024732 0ustar00runnerrunner{ "id": 1, "name": "vm-test-group", "slug": "vm-test-group" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/virtualization/cluster_groups.json0000644000175100017510000000030415136437134025121 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "vm-test-group", "slug": "vm-test-group" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/virtualization/cluster_type.json0000644000175100017510000000010715136437134024564 0ustar00runnerrunner{ "id": 1, "name": "vm-test-type", "slug": "vm-test-type" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/virtualization/cluster_types.json0000644000175100017510000000030215136437134024744 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "vm-test-type", "slug": "vm-test-type" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/virtualization/clusters.json0000644000175100017510000000154515136437134023715 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "vm-test-cluster", "type": { "id": 1, "url": "http://localhost:8000/api/virtualization/cluster-types/1/", "name": "vm-test-type", "slug": "vm-test-type" }, "group": { "id": 1, "url": "http://localhost:8000/api/virtualization/cluster-groups/1/", "name": "vm-test-group", "slug": "vm-test-group" }, "site": { "id": 1, "url": "http://localhost:8000/api/dcim/sites/1/", "name": "TEST1", "slug": "test1" }, "comments": "", "custom_fields": {} } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/virtualization/interface.json0000644000175100017510000000042715136437134024007 0ustar00runnerrunner{ "id": 223, "name": "eth0", "virtual_machine": { "id": 1, "url": "http://localhost:8000/api/virtualization/virtual-machines/1/", "name": "vm-test01" }, "enabled": true, "mac_address": null, "mtu": null, "description": "" }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/virtualization/interfaces.json0000644000175100017510000000152315136437134024170 0ustar00runnerrunner{ "count": 2, "next": null, "previous": null, "results": [ { "id": 223, "name": "eth0", "virtual_machine": { "id": 1, "url": "http://localhost:8000/api/virtualization/virtual-machines/1/", "name": "vm-test01" }, "enabled": true, "mac_address": null, "mtu": null, "description": "" }, { "id": 224, "name": "eth1", "virtual_machine": { "id": 1, "url": "http://localhost:8000/api/virtualization/virtual-machines/1/", "name": "vm-test01" }, "enabled": true, "mac_address": null, "mtu": null, "description": "" } ] }././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/virtualization/virtual_machine.json0000644000175100017510000000072015136437134025215 0ustar00runnerrunner{ "id": 1, "name": "vm-test01", "status": { "value": 1, "label": "Active" }, "cluster": { "id": 1, "url": "http://localhost:8000/api/virtualization/clusters/1/", "name": "vm-test-cluster" }, "role": null, "tenant": null, "platform": null, "primary_ip4": null, "primary_ip6": null, "vcpus": 2, "memory": 1024, "disk": 500, "comments": "", "custom_fields": {} } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/virtualization/virtual_machines.json0000644000175100017510000000133215136437134025400 0ustar00runnerrunner{ "count": 1, "next": null, "previous": null, "results": [ { "id": 1, "name": "vm-test01", "status": { "value": 1, "label": "Active" }, "cluster": { "id": 1, "url": "http://localhost:8000/api/virtualization/clusters/1/", "name": "vm-test-cluster" }, "role": null, "tenant": null, "platform": null, "primary_ip4": null, "primary_ip6": null, "vcpus": 2, "memory": 1024, "disk": 500, "comments": "", "custom_fields": {} } ] }././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8203304 pynetbox-7.6.1/tests/fixtures/wireless/0000755000175100017510000000000015136437144017723 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/wireless/wireless_lan.json0000644000175100017510000000004615136437134023304 0ustar00runnerrunner{ "id": 1, "ssid": "SSID 1" } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/fixtures/wireless/wireless_lans.json0000644000175100017510000000034015136437134023464 0ustar00runnerrunner{ "count": 2, "next": null, "previous": null, "results": [ { "id": 1, "ssid": "SSID 1" }, { "id": 2, "ssid": "SSID 2" } ] } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8203304 pynetbox-7.6.1/tests/integration/0000755000175100017510000000000015136437144016540 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/integration/conftest.py0000644000175100017510000004561215136437134020746 0ustar00runnerrunnerimport atexit import os import subprocess as subp import time from http.client import RemoteDisconnected import pytest import requests import yaml import pynetbox DOCKER_PROJECT_PREFIX = "pytest_pynetbox" def get_netbox_docker_version_tag(netbox_version): """Get the repo tag to build netbox-docker in from the requested netbox version. Args: netbox_version (version.Version): The version of netbox we want to build Returns: str: The release tag for the netbox-docker repo that should be able to build the requested version of netbox. """ major, minor = netbox_version.major, netbox_version.minor if (major, minor) == (4, 2): tag = "3.2.1" elif (major, minor) == (4, 3): tag = "3.3.0" elif (major, minor) == (4, 4): tag = "3.4.2" else: raise NotImplementedError( "Version %s is not currently supported" % netbox_version ) return tag @pytest.fixture(scope="session") def git_toplevel(): """Get the top level of the current git repo. Returns: str: The path of the top level directory of the current git repo. """ try: subp.check_call(["which", "git"]) except subp.CalledProcessError: pytest.skip(reason="git executable was not found on the host") return ( subp.check_output(["git", "rev-parse", "--show-toplevel"]) .decode("utf-8") .splitlines()[0] ) @pytest.fixture(scope="session") def netbox_docker_repo_dirpaths(pytestconfig, git_toplevel): """Get the path to the netbox-docker repos we will use. Returns: dict: A map of the repo dir paths to the versions of netbox that should be run from that repo as: { : [ , ..., ] } """ try: subp.check_call(["which", "docker"]) except subp.CalledProcessError: pytest.skip(reason="docker executable was not found on the host") netbox_versions_by_repo_dirpaths = {} for netbox_version in pytestconfig.option.netbox_versions: repo_version_tag = get_netbox_docker_version_tag(netbox_version=netbox_version) print("top: ", git_toplevel) repo_fpath = os.path.join( git_toplevel, ".netbox-docker-%s" % str(repo_version_tag) ) if os.path.isdir(repo_fpath): subp.check_call( ["git", "fetch"], cwd=repo_fpath, stdout=subp.PIPE, stderr=subp.PIPE ) subp.check_call( ["git", "reset", "--hard"], cwd=repo_fpath, stdout=subp.PIPE, stderr=subp.PIPE, ) subp.check_call( ["git", "pull", "origin", "release"], cwd=repo_fpath, stdout=subp.PIPE, stderr=subp.PIPE, ) else: subp.check_call( [ "git", "clone", "https://github.com/netbox-community/netbox-docker", repo_fpath, ], cwd=git_toplevel, stdout=subp.PIPE, stderr=subp.PIPE, ) subp.check_call( ["git", "checkout", repo_version_tag], cwd=repo_fpath, stdout=subp.PIPE, stderr=subp.PIPE, ) try: netbox_versions_by_repo_dirpaths[repo_fpath].append(netbox_version) except KeyError: netbox_versions_by_repo_dirpaths[repo_fpath] = [netbox_version] return netbox_versions_by_repo_dirpaths @pytest.fixture(scope="session") def docker_compose_project_name(pytestconfig): """Get the project name to use for docker containers. This will return a consistently generated project name so we can kill stale containers after the test run is finished. """ return "%s_%s" % (DOCKER_PROJECT_PREFIX, int(time.time())) def clean_netbox_docker_tmpfiles(): """Clean up any temporary files created in the netbox-docker repo.""" dirpath, dirnames, filenames = next(os.walk("./")) for filename in filenames: if filename.startswith("docker-compose-v"): os.remove(filename) def clean_docker_objects(): """Clean up any docker objects created via these tests.""" # clean up any containers for line in subp.check_output(["docker", "ps", "-a"]).decode("utf-8").splitlines(): words = line.split() if not words: continue if words[-1].startswith(DOCKER_PROJECT_PREFIX): subp.check_call( ["docker", "rm", "-f", words[0]], stdout=subp.PIPE, stderr=subp.PIPE ) # clean up any volumes for line in ( subp.check_output(["docker", "volume", "list"]).decode("utf-8").splitlines() ): words = line.split() if not words: continue if words[-1].startswith(DOCKER_PROJECT_PREFIX): subp.check_call( ["docker", "volume", "rm", "-f", words[-1]], stdout=subp.PIPE, stderr=subp.PIPE, ) # clean up any networks for line in ( subp.check_output(["docker", "network", "list"]).decode("utf-8").splitlines() ): words = line.split() if not words: continue if words[1].startswith(DOCKER_PROJECT_PREFIX): subp.check_call( ["docker", "network", "rm", words[1]], stdout=subp.PIPE, stderr=subp.PIPE, ) # TODO: this function could be split up @pytest.fixture(scope="session") def docker_compose_file(pytestconfig, netbox_docker_repo_dirpaths): """Return paths to the compose files needed to create test containers. We can create container sets for multiple versions of netbox here by returning a list of paths to multiple compose files. """ clean_netbox_docker_tmpfiles() clean_docker_objects() compose_files = [] for ( netbox_docker_repo_dirpath, netbox_versions, ) in netbox_docker_repo_dirpaths.items(): compose_source_fpath = os.path.join( netbox_docker_repo_dirpath, "docker-compose.yml" ) for netbox_version in netbox_versions: # check for updates to the local netbox images subp.check_call( ["docker", "pull", "netboxcommunity/netbox:v%s" % (netbox_version)], stdout=subp.PIPE, stderr=subp.PIPE, ) docker_netbox_version = str(netbox_version).replace(".", "_") # load the compose file yaml compose_data = yaml.safe_load(open(compose_source_fpath, "r").read()) # add the custom network for this version docker_network_name = "%s_v%s" % ( DOCKER_PROJECT_PREFIX, docker_netbox_version, ) compose_data["networks"] = {docker_network_name: {}} # https://docs.docker.com/compose/compose-file/compose-file-v3/#network-configuration-reference if "version" not in compose_data or compose_data["version"] >= "3.5": compose_data["networks"][docker_network_name][ "name" ] = docker_network_name # prepend the netbox version to each of the service names and anything else # needed to make the continers unique to the netbox version new_services = {} for service_name in compose_data["services"].keys(): new_service_name = "netbox_v%s_%s" % ( docker_netbox_version, service_name, ) new_services[new_service_name] = compose_data["services"][service_name] if service_name in ["netbox", "netbox-worker"]: # set the netbox image version new_services[new_service_name]["image"] = ( "netboxcommunity/netbox:v%s" % netbox_version ) new_services[new_service_name]["environment"] = { "SKIP_SUPERUSER": "false", "SUPERUSER_API_TOKEN": "0123456789abcdef0123456789abcdef01234567", "SUPERUSER_EMAIL": "admin@example.com", "SUPERUSER_NAME": "admin", "SUPERUSER_PASSWORD": "admin", } if service_name == "netbox": # ensure the netbox container listens on a random port new_services[new_service_name]["ports"] = ["8080"] # Increase health check timeouts for GitHub Actions runners # which may have more resource constraints new_services[new_service_name]["healthcheck"] = { "test": "curl -f http://localhost:8080/login/ || exit 1", "start_period": "180s", # Increased from 90s "timeout": "10s", # Increased from 3s "interval": "15s", "retries": 5, } # set the network and an alias to the proper short name of the container # within that network new_services[new_service_name]["networks"] = { docker_network_name: {"aliases": [service_name]} } # fix the naming of any dependencies if "depends_on" in new_services[new_service_name]: new_service_dependencies = [] for dependent_service_name in new_services[new_service_name][ "depends_on" ]: new_service_dependencies.append( "netbox_v%s_%s" % ( docker_netbox_version, dependent_service_name, ) ) new_services[new_service_name][ "depends_on" ] = new_service_dependencies # make any internal named volumes unique to the netbox version if "volumes" in new_services[new_service_name]: new_volumes = [] for volume_config in new_services[new_service_name]["volumes"]: source = volume_config.split(":")[0] if "/" in source: if volume_config.startswith("./"): # Set the full path to the volume source. Without this # some of the containers would be spun up from the # wrong source directories. volume_source, volume_dest = volume_config.split( ":", maxsplit=1 ) volume_source = os.path.join( netbox_docker_repo_dirpath, volume_source[2::] ) new_volumes.append( ":".join([volume_source, volume_dest]) ) else: new_volumes.append(volume_config) else: new_volumes.append( "%s_v%s_%s" % ( DOCKER_PROJECT_PREFIX, docker_netbox_version, volume_config, ) ) new_services[new_service_name]["volumes"] = new_volumes # replace the services config with the renamed versions compose_data["services"] = new_services # prepend local volume names new_volumes = {} for volume_name, volume_config in compose_data["volumes"].items(): new_volumes[ "%s_v%s_%s" % ( DOCKER_PROJECT_PREFIX, docker_netbox_version, volume_name, ) ] = volume_config compose_data["volumes"] = new_volumes compose_output_fpath = os.path.join( netbox_docker_repo_dirpath, "docker-compose-v%s.yml" % netbox_version, ) with open(compose_output_fpath, "w") as fdesc: fdesc.write(yaml.dump(compose_data)) compose_files.append(compose_output_fpath) # set post=run cleanup hooks if requested if pytestconfig.option.cleanup: atexit.register(clean_docker_objects) atexit.register(clean_netbox_docker_tmpfiles) return compose_files def netbox_is_responsive(url): """Check if the HTTP service is up and responsive.""" try: response = requests.get(url) if response.status_code == 200: return True except ( ConnectionError, ConnectionResetError, requests.exceptions.ConnectionError, RemoteDisconnected, ): return False def id_netbox_service(fixture_value): """Create and ID representation for a netbox service fixture param. Returns: str: Identifiable representation of the service, as best we can """ return "netbox v%s" % fixture_value @pytest.fixture(scope="session") def docker_netbox_service( pytestconfig, docker_ip, docker_services, request, ): """Get the netbox service to test against. This function waits until the netbox container is fully up and running then does an initial data population with a few object types to be used in testing. Then the service is returned as a fixture to be called from tests. """ netbox_integration_version = request.param netbox_service_name = "netbox_v%s_netbox" % str(netbox_integration_version).replace( ".", "_" ) netbox_service_port = 8080 try: # `port_for` takes a container port and returns the corresponding host port port = docker_services.port_for(netbox_service_name, netbox_service_port) except Exception as err: docker_ps_stdout = subp.check_output(["docker", "ps", "-a"]).decode("utf-8") exited_container_logs = [] for line in docker_ps_stdout.splitlines(): if "Exited" in line: container_id = line.split()[0] exited_container_logs.append( "\nContainer %s logs:\n%s" % ( container_id, subp.check_output(["docker", "logs", container_id]).decode( "utf-8" ), ) ) raise KeyError( "Unable to find a docker service matching the name %s on port %s. Running" " containers: %s. Original error: %s. Logs:\n%s" % ( netbox_service_name, netbox_service_port, docker_ps_stdout, err, exited_container_logs, ) ) url = "http://{}:{}".format(docker_ip, port) docker_services.wait_until_responsive( timeout=300.0, pause=1, check=lambda: netbox_is_responsive(url) ) return { "url": url, "netbox_version": netbox_integration_version, } @pytest.fixture(scope="session") def api(docker_netbox_service): return pynetbox.api( docker_netbox_service["url"], token="0123456789abcdef0123456789abcdef01234567" ) @pytest.fixture(scope="session") def nb_version(docker_netbox_service): return docker_netbox_service["netbox_version"] @pytest.fixture(scope="session") def site(api): site = api.dcim.sites.create(name="test", slug="test") yield site site.delete() @pytest.fixture(scope="session") def manufacturer(api): manufacturer = api.dcim.manufacturers.create( name="test-manufacturer", slug="test-manufacturer" ) yield manufacturer manufacturer.delete() @pytest.fixture(scope="session") def device_type(api, manufacturer): device_type = api.dcim.device_types.create( manufacturer=manufacturer.id, model="test-device-type", slug="test-device-type", height=1, ) yield device_type device_type.delete() @pytest.fixture(scope="session") def role(api): role = api.dcim.device_roles.create( name="test-device-role", slug="test-device-role", color="000000", ) yield role role.delete() def create_device(api, site, device_type, role, name): """Helper function to create a device with proper version handling. Args: api: The API instance site: Site object device_type: DeviceType object role: DeviceRole object name: Device name Returns: Created device object """ from packaging import version if version.parse(api.version) >= version.parse("3.6"): return api.dcim.devices.create( name=name, role=role.id, device_type=device_type.id, site=site.id, ) else: return api.dcim.devices.create( name=name, device_role=role.id, device_type=device_type.id, site=site.id, ) def pytest_generate_tests(metafunc): """Dynamically parametrize some functions based on args from the cli parser.""" if "docker_netbox_service" in metafunc.fixturenames: # parametrize the requested versions of netbox to the docker_netbox_services fixture # so that it will return a fixture for each of the versions requested # individually rather than one fixture with multiple versions within it metafunc.parametrize( "docker_netbox_service", metafunc.config.getoption("netbox_versions"), ids=id_netbox_service, indirect=True, ) @pytest.fixture(scope="session") def docker_cleanup(pytestconfig): """Override the docker cleanup command for the containsers used in testing.""" # pytest-docker does not always clean up after itself properly, and sometimes it # will fail during cleanup because there is still a connection to one of the # running containers. Here we will disable the builtin cleanup of containers via the # pytest-docker module and implement our own instead. # This is only relevant until https://github.com/avast/pytest-docker/pull/33 gets # resolved. # There is not a great way to skip the shutdown step, so in this case to skip # it we will just pass the "version" arg so the containers are left alone command_args = "version" return command_args ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/integration/test_circuits.py0000644000175100017510000001775215136437134022011 0ustar00runnerrunnerimport pytest @pytest.fixture(scope="module") def provider(api): provider = api.circuits.providers.create(name="test-provider", slug="test-provider") yield provider provider.delete() @pytest.fixture(scope="module") def circuit_type(api): circuit_type = api.circuits.circuit_types.create( name="test-circuit-type", slug="test-circuit-type" ) yield circuit_type circuit_type.delete() @pytest.fixture(scope="module") def circuit(api, provider, circuit_type): circuit = api.circuits.circuits.create( cid="TEST-CIRCUIT-001", provider=provider.id, type=circuit_type.id ) yield circuit circuit.delete() @pytest.fixture(scope="module") def provider_network(api, provider): provider_network = api.circuits.provider_networks.create( name="test-provider-network", provider=provider.id ) yield provider_network provider_network.delete() @pytest.fixture(scope="module") def virtual_circuit_type(api): virtual_circuit_type = api.circuits.virtual_circuit_types.create( name="test-virtual-circuit-type", slug="test-virtual-circuit-type" ) yield virtual_circuit_type virtual_circuit_type.delete() @pytest.fixture(scope="module") def virtual_circuit(api, provider_network, virtual_circuit_type): virtual_circuit = api.circuits.virtual_circuits.create( cid="TEST-VCIRCUIT-001", provider_network=provider_network.id, type=virtual_circuit_type.id, ) yield virtual_circuit virtual_circuit.delete() @pytest.mark.usefixtures("init") class BaseTest: app = "circuits" def _init_helper( self, request, fixture, update_field=None, filter_kwargs=None, endpoint=None, str_repr=None, ): request.cls.endpoint = endpoint request.cls.fixture = fixture request.cls.update_field = update_field request.cls.filter_kwargs = filter_kwargs request.cls.str_repr = str_repr def test_create(self): assert self.fixture def test_str(self): if self.str_repr: test = str(self.fixture) assert test == self.str_repr def test_update_fixture(self): if self.update_field: setattr(self.fixture, self.update_field, "Test Value") assert self.fixture.save() def test_get_fixture_by_id(self, api): test = getattr(getattr(api, self.app), self.endpoint).get(self.fixture.id) assert test if self.update_field: assert getattr(test, self.update_field) == "Test Value" def test_get_fixture_by_kwarg(self, api): test = getattr(getattr(api, self.app), self.endpoint).get(**self.filter_kwargs) assert test if self.update_field: assert getattr(test, self.update_field) == "Test Value" def test_filter_fixture(self, api): test = list( getattr(getattr(api, self.app), self.endpoint).filter(**self.filter_kwargs) )[0] assert test if self.update_field: assert getattr(test, self.update_field) == "Test Value" class TestCircuit(BaseTest): @pytest.fixture(scope="class") def init(self, request, circuit): self._init_helper( request, circuit, filter_kwargs={"cid": circuit.cid}, update_field="description", endpoint="circuits", str_repr=circuit.cid, ) class TestCircuitTermination(BaseTest): @pytest.fixture(scope="class") def site_b(self, api): site_b = api.dcim.sites.create(name="test-site-b", slug="test-site-b") yield site_b site_b.delete() @pytest.fixture(scope="class") def circuit_termination_a(self, api, circuit, site): ret = api.circuits.circuit_terminations.create( circuit=circuit.id, site=site.id, term_side="A", termination_type="dcim.site", termination_id=site.id, ) yield ret @pytest.fixture(scope="class") def circuit_termination_b(self, api, circuit, site_b): ret = api.circuits.circuit_terminations.create( circuit=circuit.id, site=site_b.id, term_side="Z", termination_type="dcim.site", termination_id=site_b.id, ) yield ret @pytest.fixture(scope="class") def init( self, request, circuit_termination_a, ): self._init_helper( request, circuit_termination_a, filter_kwargs={"circuit_id": circuit_termination_a.circuit.id}, endpoint="circuit_terminations", str_repr=circuit_termination_a.circuit.cid, ) def test_circuit_termination_paths(self, circuit_termination_a): paths_result = circuit_termination_a.paths() assert isinstance(paths_result, list) # Circuit terminations may have paths if connected through patch panels # For this test, we just verify the method works and returns correct structure if paths_result: for path in paths_result: assert "origin" in path assert "destination" in path assert "path" in path assert isinstance(path["path"], list) class TestVirtualCircuit(BaseTest): @pytest.fixture(scope="class") def init(self, request, virtual_circuit): self._init_helper( request, virtual_circuit, filter_kwargs={"cid": virtual_circuit.cid}, update_field="description", endpoint="virtual_circuits", str_repr=virtual_circuit.cid, ) class TestVirtualCircuitTermination(BaseTest): @pytest.fixture(scope="class") def device(self, api, site, device_type, role): from tests.integration.conftest import create_device device = create_device(api, site, device_type, role, "test-vcircuit-device") yield device device.delete() @pytest.fixture(scope="class") def interface_a(self, api, device): ret = api.dcim.interfaces.create( name="vlan100", type="virtual", device=device.id ) yield ret @pytest.fixture(scope="class") def interface_b(self, api, device): ret = api.dcim.interfaces.create( name="vlan200", type="virtual", device=device.id ) yield ret @pytest.fixture(scope="class") def virtual_circuit_termination_a(self, api, virtual_circuit, interface_a): ret = api.circuits.virtual_circuit_terminations.create( virtual_circuit=virtual_circuit.id, role="hub", interface=interface_a.id ) yield ret @pytest.fixture(scope="class") def virtual_circuit_termination_b(self, api, virtual_circuit, interface_b): ret = api.circuits.virtual_circuit_terminations.create( virtual_circuit=virtual_circuit.id, role="spoke", interface=interface_b.id ) yield ret @pytest.fixture(scope="class") def init( self, request, virtual_circuit_termination_a, ): self._init_helper( request, virtual_circuit_termination_a, filter_kwargs={ "virtual_circuit_id": virtual_circuit_termination_a.virtual_circuit.id }, endpoint="virtual_circuit_terminations", str_repr=virtual_circuit_termination_a.virtual_circuit.cid, ) def test_virtual_circuit_termination_paths(self, virtual_circuit_termination_a): paths_result = virtual_circuit_termination_a.paths() assert isinstance(paths_result, list) # Virtual circuit terminations may have paths if they traverse physical infrastructure # For this test, we just verify the method works and returns correct structure if paths_result: for path in paths_result: assert "origin" in path assert "destination" in path assert "path" in path assert isinstance(path["path"], list) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/integration/test_dcim.py0000644000175100017510000003256515136437134021077 0ustar00runnerrunnerimport pytest from packaging import version import pynetbox @pytest.fixture(scope="module") def rack(api, site): rack = api.dcim.racks.create(site=site.id, name="test-rack") yield rack rack.delete() @pytest.fixture(scope="module") def device(api, site, device_type, role): from tests.integration.conftest import create_device device = create_device(api, site, device_type, role, "test-device") yield device device.delete() @pytest.mark.usefixtures("init") class BaseTest: app = "dcim" def _init_helper( self, request, fixture, update_field=None, filter_kwargs=None, endpoint=None, str_repr=None, ): request.cls.endpoint = endpoint request.cls.fixture = fixture request.cls.update_field = update_field request.cls.filter_kwargs = filter_kwargs request.cls.str_repr = str_repr def test_create(self): assert self.fixture def test_str(self): if self.str_repr: test = str(self.fixture) assert test == self.str_repr def test_update_fixture(self): if self.update_field: setattr(self.fixture, self.update_field, "Test Value") assert self.fixture.save() def test_get_fixture_by_id(self, api): test = getattr(getattr(api, self.app), self.endpoint).get(self.fixture.id) assert test if self.update_field: assert getattr(test, self.update_field) == "Test Value" def test_get_fixture_by_kwarg(self, api): test = getattr(getattr(api, self.app), self.endpoint).get(**self.filter_kwargs) assert test if self.update_field: assert getattr(test, self.update_field) == "Test Value" def test_filter_fixture(self, api): test = list( getattr(getattr(api, self.app), self.endpoint).filter(**self.filter_kwargs) )[0] assert test if self.update_field: assert getattr(test, self.update_field) == "Test Value" class TestSite(BaseTest): @pytest.fixture(scope="class") def init(self, request, site): self._init_helper( request, site, filter_kwargs={"name": "test"}, update_field="description", endpoint="sites", ) @pytest.fixture(scope="class") def add_sites(self, api): sites = api.dcim.sites.create( [ {"name": "test{}".format(i), "slug": "test{}".format(i)} for i in range(2, 20) ] ) yield for i in sites: i.delete() def test_threading_duplicates(self, docker_netbox_service, add_sites): api = pynetbox.api( docker_netbox_service["url"], token="0123456789abcdef0123456789abcdef01234567", threading=True, ) test = api.dcim.sites.all(limit=5) test_list = list(test) test_set = set(test_list) assert len(test_list) == len(test_set) class TestRack(BaseTest): @pytest.fixture(scope="class") def init(self, request, rack): self._init_helper( request, rack, filter_kwargs={"name": rack.name}, update_field="comments", endpoint="racks", ) def test_get_elevation(self): test = self.fixture.elevation.list() assert test assert isinstance(test, list) class TestManufacturer(BaseTest): @pytest.fixture(scope="class") def init(self, request, manufacturer, nb_version): self._init_helper( request, manufacturer, filter_kwargs={"name": manufacturer.name}, update_field="description" if version.parse("2.10") < nb_version else None, endpoint="manufacturers", ) class TestDeviceType(BaseTest): @pytest.fixture(scope="class") def init(self, request, device_type): self._init_helper( request, device_type, filter_kwargs={"model": device_type.model}, update_field="comments", endpoint="device_types", str_repr=device_type.model, ) class TestDevice(BaseTest): @pytest.fixture(scope="class") def init(self, request, device): self._init_helper( request, device, filter_kwargs={"name": device.name}, update_field="comments", endpoint="devices", ) class TestInterface(BaseTest): @pytest.fixture(scope="class") def interface(self, api, device): ret = api.dcim.interfaces.create( name="test-interface", type="1000base-t", device=device.id ) yield ret ret.delete() @pytest.fixture(scope="class") def init(self, request, interface): self._init_helper( request, interface, filter_kwargs={"name": interface.name}, update_field="description", endpoint="interfaces", ) class TestPowerCable(BaseTest): @pytest.fixture(scope="class") def power_outlet(self, api, device_type, role, site): from tests.integration.conftest import create_device pdu = create_device(api, site, device_type, role, "test-pdu") outlet = api.dcim.power_outlets.create(name="outlet", device=pdu.id) yield outlet pdu.delete() @pytest.fixture(scope="class") def power_port(self, api, device): ret = api.dcim.power_ports.create(name="PSU1", device=device.id) yield ret @pytest.fixture(scope="class") def power_cable(self, api, power_outlet, power_port): cable = api.dcim.cables.create( a_terminations=[ {"object_type": "dcim.powerport", "object_id": power_port.id}, ], b_terminations=[ {"object_type": "dcim.poweroutlet", "object_id": power_outlet.id}, ], ) yield cable cable.delete() @pytest.fixture(scope="class") def init(self, request, power_cable): self._init_helper( request, power_cable, filter_kwargs={"id": power_cable.id}, endpoint="cables", str_repr="PSU1 <> outlet", ) class TestConsoleCable(BaseTest): @pytest.fixture(scope="class") def console_server_port(self, api, device_type, role, site): from tests.integration.conftest import create_device device = create_device(api, site, device_type, role, "test-console-server") ret = api.dcim.console_server_ports.create(name="Port 1", device=device.id) yield ret device.delete() @pytest.fixture(scope="class") def console_port(self, api, device): ret = api.dcim.console_ports.create(name="Console", device=device.id) yield ret @pytest.fixture(scope="class") def console_cable(self, api, console_port, console_server_port): ret = api.dcim.cables.create( a_terminations=[ {"object_type": "dcim.consoleport", "object_id": console_port.id}, ], b_terminations=[ { "object_type": "dcim.consoleserverport", "object_id": console_server_port.id, }, ], ) yield ret ret.delete() @pytest.fixture(scope="class") def init(self, request, console_cable): self._init_helper( request, console_cable, filter_kwargs={"id": console_cable.id}, endpoint="cables", str_repr="Console <> Port 1", ) class TestInterfaceCable(BaseTest): @pytest.fixture(scope="class") def interface_b(self, api, device_type, role, site): from tests.integration.conftest import create_device device = create_device(api, site, device_type, role, "test-device-2") ret = api.dcim.interfaces.create( name="Ethernet1", type="1000base-t", device=device.id ) yield ret device.delete() @pytest.fixture(scope="class") def interface_a(self, api, device): ret = api.dcim.interfaces.create( name="Ethernet1", type="1000base-t", device=device.id ) yield ret @pytest.fixture(scope="class") def interface_cable(self, api, interface_a, interface_b): ret = api.dcim.cables.create( a_terminations=[ {"object_type": "dcim.interface", "object_id": interface_a.id}, ], b_terminations=[ {"object_type": "dcim.interface", "object_id": interface_b.id}, ], ) yield ret ret.delete() @pytest.fixture(scope="class") def init(self, request, interface_cable): self._init_helper( request, interface_cable, filter_kwargs={"id": interface_cable.id}, endpoint="cables", str_repr="Ethernet1 <> Ethernet1", ) def test_trace(self, interface_a): test = interface_a.trace() assert test assert test[0][0].name == "Ethernet1" assert test[2][0].name == "Ethernet1" class TestPassThroughPorts(BaseTest): @pytest.fixture(scope="class") def device_a(self, api, device_type, role, site): from tests.integration.conftest import create_device device = create_device(api, site, device_type, role, "test-device-a") yield device device.delete() @pytest.fixture(scope="class") def device_b(self, api, device_type, role, site): from tests.integration.conftest import create_device device = create_device(api, site, device_type, role, "test-device-b") yield device device.delete() @pytest.fixture(scope="class") def front_port_a(self, api, device_a, rear_port_a): ret = api.dcim.front_ports.create( name="FrontPort1", device=device_a.id, type="8p8c", rear_port=rear_port_a.id, rear_port_position=1, ) yield ret @pytest.fixture(scope="class") def rear_port_a(self, api, device_a): ret = api.dcim.rear_ports.create( name="RearPort1", device=device_a.id, type="8p8c", positions=1 ) yield ret @pytest.fixture(scope="class") def front_port_b(self, api, device_b, rear_port_b): ret = api.dcim.front_ports.create( name="FrontPort2", device=device_b.id, type="8p8c", rear_port=rear_port_b.id, rear_port_position=1, ) yield ret @pytest.fixture(scope="class") def rear_port_b(self, api, device_b): ret = api.dcim.rear_ports.create( name="RearPort2", device=device_b.id, type="8p8c", positions=1 ) yield ret @pytest.fixture(scope="class") def interface_a(self, api, device_a): ret = api.dcim.interfaces.create( name="eth0", type="1000base-t", device=device_a.id ) yield ret @pytest.fixture(scope="class") def interface_b(self, api, device_b): ret = api.dcim.interfaces.create( name="eth0", type="1000base-t", device=device_b.id ) yield ret @pytest.fixture(scope="class") def cable_front(self, api, front_port_a, front_port_b): ret = api.dcim.cables.create( a_terminations=[ {"object_type": "dcim.frontport", "object_id": front_port_a.id}, ], b_terminations=[ {"object_type": "dcim.frontport", "object_id": front_port_b.id}, ], ) yield ret ret.delete() @pytest.fixture(scope="class") def cable_rear_a(self, api, rear_port_a, interface_a): ret = api.dcim.cables.create( a_terminations=[ {"object_type": "dcim.rearport", "object_id": rear_port_a.id}, ], b_terminations=[ {"object_type": "dcim.interface", "object_id": interface_a.id}, ], ) yield ret ret.delete() @pytest.fixture(scope="class") def cable_rear_b(self, api, rear_port_b, interface_b): ret = api.dcim.cables.create( a_terminations=[ {"object_type": "dcim.rearport", "object_id": rear_port_b.id}, ], b_terminations=[ {"object_type": "dcim.interface", "object_id": interface_b.id}, ], ) yield ret ret.delete() @pytest.fixture(scope="class") def init( self, request, front_port_a, cable_front, cable_rear_a, cable_rear_b, ): self._init_helper( request, front_port_a, filter_kwargs={"name": front_port_a.name}, endpoint="front_ports", ) def test_front_port_paths(self, front_port_a): paths_result = front_port_a.paths() assert paths_result assert isinstance(paths_result, list) assert len(paths_result) > 0 # Each path should have origin, destination, and path keys for path in paths_result: assert "origin" in path assert "destination" in path assert "path" in path assert isinstance(path["path"], list) def test_rear_port_paths(self, rear_port_a): paths_result = rear_port_a.paths() assert paths_result assert isinstance(paths_result, list) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/integration/test_ipam.py0000644000175100017510000001163615136437134021105 0ustar00runnerrunnerimport pytest from packaging import version @pytest.mark.usefixtures("init") class BaseTest: app = "ipam" def _init_helper( self, request, fixture, update_field=None, filter_kwargs=None, endpoint=None, str_repr=None, ): request.cls.endpoint = endpoint request.cls.fixture = fixture request.cls.update_field = update_field request.cls.filter_kwargs = filter_kwargs request.cls.str_repr = str_repr def test_create(self): assert self.fixture def test_str(self): if self.str_repr: test = str(self.fixture) assert test == self.str_repr def test_update_fixture(self): if self.update_field: setattr(self.fixture, self.update_field, "Test Value") assert self.fixture.save() def test_get_fixture_by_id(self, api): test = getattr(getattr(api, self.app), self.endpoint).get(self.fixture.id) assert test if self.update_field: assert getattr(test, self.update_field) == "Test Value" def test_get_fixture_by_kwarg(self, api): test = getattr(getattr(api, self.app), self.endpoint).get(**self.filter_kwargs) assert test if self.update_field: assert getattr(test, self.update_field) == "Test Value" def test_filter_fixture(self, api): test = list( getattr(getattr(api, self.app), self.endpoint).filter(**self.filter_kwargs) )[0] assert test if self.update_field: assert getattr(test, self.update_field) == "Test Value" @pytest.fixture(scope="module") def rir(api, site): ret = api.ipam.rirs.create( name="ministry-of-silly-walks", slug="ministry-of-silly-walks" ) yield ret ret.delete() class TestRIR(BaseTest): @pytest.fixture(scope="class") def init(self, request, rir, nb_version): self._init_helper( request, rir, filter_kwargs={"name": "ministry-of-silly-walks"}, update_field="description" if nb_version >= version.parse("2.8") else None, endpoint="rirs", ) class TestAggregate(BaseTest): @pytest.fixture(scope="class") def aggregate(self, api, rir): ret = api.ipam.aggregates.create(prefix="192.0.2.0/24", rir=rir.id) yield ret ret.delete() @pytest.fixture(scope="class") def init(self, request, aggregate): self._init_helper( request, aggregate, filter_kwargs={"prefix": "192.0.2.0/24"}, update_field="description", endpoint="aggregates", ) class TestPrefix(BaseTest): @pytest.fixture(scope="class") def prefix(self, api): ret = api.ipam.prefixes.create(prefix="192.0.2.0/24") yield ret ret.delete() @pytest.fixture(scope="class") def init(self, request, prefix): self._init_helper( request, prefix, filter_kwargs={"prefix": "192.0.2.0/24"}, update_field="description", endpoint="prefixes", ) class TestIpAddress(BaseTest): @pytest.fixture(scope="class") def ip(self, api): ret = api.ipam.ip_addresses.create(address="192.0.2.1/24") yield ret ret.delete() @pytest.fixture(scope="class") def init(self, request, ip): self._init_helper( request, ip, filter_kwargs={"q": "192.0.2.1/24"}, update_field="description", endpoint="ip_addresses", ) class TestRole(BaseTest): @pytest.fixture(scope="class") def role(self, api): ret = api.ipam.roles.create(name="test-role", slug="test-role") yield ret ret.delete() @pytest.fixture(scope="class") def init(self, request, role): self._init_helper( request, role, filter_kwargs={"name": "test-role"}, update_field="description", endpoint="roles", ) class TestVlan(BaseTest): @pytest.fixture(scope="class") def vlan(self, api): ret = api.ipam.vlans.create(vid=123, name="test-vlan") yield ret ret.delete() @pytest.fixture(scope="class") def init(self, request, vlan): self._init_helper( request, vlan, filter_kwargs={"name": "test-vlan"}, update_field="description", endpoint="vlans", ) class TestVRF(BaseTest): @pytest.fixture(scope="class") def vrf(self, api): ret = api.ipam.vrfs.create(name="test-vrf", rd="192.0.2.1:1234") yield ret ret.delete() @pytest.fixture(scope="class") def init(self, request, vrf): self._init_helper( request, vrf, filter_kwargs={"name": "test-vrf"}, update_field="description", endpoint="vrfs", ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/test_api.py0000644000175100017510000000532115136437134016377 0ustar00runnerrunnerimport unittest from unittest.mock import patch import pynetbox from .util import Response host = "http://localhost:8000" def_kwargs = { "token": "abc123", } # Keys are app names, values are arbitrarily selected endpoints # We use dcim and ipam since they have unique app classes # and circuits because it does not. We don't add other apps/endpoints # beyond 'circuits' as they all use the same code as each other endpoints = { "dcim": "devices", "ipam": "prefixes", "circuits": "circuits", } class ApiTestCase(unittest.TestCase): @patch( "requests.sessions.Session.post", return_value=Response(), ) def test_get(self, *_): api = pynetbox.api(host, **def_kwargs) self.assertTrue(api) @patch( "requests.sessions.Session.post", return_value=Response(), ) def test_sanitize_url(self, *_): api = pynetbox.api("http://localhost:8000/", **def_kwargs) self.assertTrue(api) self.assertEqual(api.base_url, "http://localhost:8000/api") class ApiVersionTestCase(unittest.TestCase): class ResponseHeadersWithVersion: headers = {"API-Version": "1.999"} ok = True @patch( "requests.sessions.Session.get", return_value=ResponseHeadersWithVersion(), ) def test_api_version(self, *_): api = pynetbox.api( host, ) self.assertEqual(api.version, "1.999") class ResponseHeadersWithoutVersion: headers = {} ok = True @patch( "requests.sessions.Session.get", return_value=ResponseHeadersWithoutVersion(), ) def test_api_version_not_found(self, *_): api = pynetbox.api( host, ) self.assertEqual(api.version, "") class ApiStatusTestCase(unittest.TestCase): class ResponseWithStatus: ok = True def json(self): return { "netbox-version": "0.9.9", } @patch( "requests.sessions.Session.get", return_value=ResponseWithStatus(), ) def test_api_status(self, *_): api = pynetbox.api( host, ) self.assertEqual(api.status()["netbox-version"], "0.9.9") class ApiCreateTokenTestCase(unittest.TestCase): @patch( "requests.sessions.Session.post", return_value=Response(fixture="api/token_provision.json"), ) def test_create_token(self, *_): api = pynetbox.api(host) token = api.create_token("user", "pass") self.assertTrue(isinstance(token, pynetbox.core.response.Record)) self.assertEqual(token.key, "1234567890123456789012345678901234567890") self.assertEqual(api.token, "1234567890123456789012345678901234567890") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/test_app.py0000644000175100017510000000305415136437134016407 0ustar00runnerrunnerimport unittest from unittest.mock import patch import pynetbox host = "http://localhost:8000" def_kwargs = { "token": "abc123", } class AppConfigTestCase(unittest.TestCase): @patch( "pynetbox.core.query.Request.get", return_value={ "tables": { "DeviceTable": { "columns": [ "name", "status", "tenant", "tags", ], }, }, }, ) def test_config(self, *_): api = pynetbox.api(host, **def_kwargs) config = api.users.config() self.assertEqual(sorted(config.keys()), ["tables"]) self.assertEqual( sorted(config["tables"]["DeviceTable"]["columns"]), ["name", "status", "tags", "tenant"], ) class PluginAppTestCase(unittest.TestCase): @patch( "pynetbox.core.query.Request.get", return_value=[ { "name": "test_plugin", "package": "netbox_test_plugin", } ], ) def test_installed_plugins(self, *_): api = pynetbox.api(host, **def_kwargs) plugins = api.plugins.installed_plugins() self.assertEqual(len(plugins), 1) self.assertEqual(plugins[0]["name"], "test_plugin") def test_plugin_app_name(self, *_): api = pynetbox.api(host, **def_kwargs) test_plugin = api.plugins.test_plugin self.assertEqual(test_plugin.name, "plugins/test-plugin") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/test_circuits.py0000644000175100017510000000625015136437134017455 0ustar00runnerrunnerimport unittest from unittest.mock import patch import pynetbox from .util import Response api = pynetbox.api( "http://localhost:8000", ) nb = api.circuits HEADERS = {"accept": "application/json"} class Generic: class Tests(unittest.TestCase): name = "" ret = pynetbox.core.response.Record app = "circuits" def test_get_all(self): with patch( "requests.sessions.Session.get", return_value=Response(fixture="{}/{}.json".format(self.app, self.name)), ) as mock: ret = list(getattr(nb, self.name).all()) self.assertTrue(ret) self.assertTrue(isinstance(ret[0], self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/".format( self.app, self.name.replace("_", "-") ), params={"limit": 0}, json=None, headers=HEADERS, ) def test_filter(self): with patch( "requests.sessions.Session.get", return_value=Response(fixture="{}/{}.json".format(self.app, self.name)), ) as mock: ret = list(getattr(nb, self.name).filter(name="test")) self.assertTrue(ret) self.assertTrue(isinstance(ret[0], self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/".format( self.app, self.name.replace("_", "-") ), params={"name": "test", "limit": 0}, json=None, headers=HEADERS, ) def test_get(self): with patch( "requests.sessions.Session.get", return_value=Response( fixture="{}/{}.json".format(self.app, self.name[:-1]) ), ) as mock: ret = getattr(nb, self.name).get(1) self.assertTrue(ret) self.assertTrue(isinstance(ret, self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/1/".format( self.app, self.name.replace("_", "-") ), params={}, json=None, headers=HEADERS, ) class CircuitsTestCase(Generic.Tests): name = "circuits" @patch( "requests.sessions.Session.get", return_value=Response(fixture="circuits/circuit.json"), ) def test_repr(self, _): test = nb.circuits.get(1) self.assertEqual(str(test), "123456") class ProviderTestCase(Generic.Tests): name = "providers" class CircuitTypeTestCase(Generic.Tests): name = "circuit_types" class CircuitTerminationsTestCase(Generic.Tests): name = "circuit_terminations" @patch( "requests.sessions.Session.get", return_value=Response(fixture="circuits/circuit_termination.json"), ) def test_repr(self, _): test = nb.circuit_terminations.get(1) self.assertEqual(str(test), "123456") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/test_tenancy.py0000644000175100017510000000504715136437134017274 0ustar00runnerrunnerimport unittest from unittest.mock import patch import pynetbox from .util import Response api = pynetbox.api( "http://localhost:8000", ) nb = api.tenancy HEADERS = {"accept": "application/json"} class Generic: class Tests(unittest.TestCase): name = "" ret = pynetbox.core.response.Record app = "tenancy" def test_get_all(self): with patch( "requests.sessions.Session.get", return_value=Response(fixture="{}/{}.json".format(self.app, self.name)), ) as mock: ret = list(getattr(nb, self.name).all()) self.assertTrue(ret) self.assertTrue(isinstance(ret[0], self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/".format( self.app, self.name.replace("_", "-") ), params={"limit": 0}, json=None, headers=HEADERS, ) def test_filter(self): with patch( "requests.sessions.Session.get", return_value=Response(fixture="{}/{}.json".format(self.app, self.name)), ) as mock: ret = list(getattr(nb, self.name).filter(name="test")) self.assertTrue(ret) self.assertTrue(isinstance(ret[0], self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/".format( self.app, self.name.replace("_", "-") ), params={"name": "test", "limit": 0}, json=None, headers=HEADERS, ) def test_get(self): with patch( "requests.sessions.Session.get", return_value=Response( fixture="{}/{}.json".format(self.app, self.name[:-1]) ), ) as mock: ret = getattr(nb, self.name).get(1) self.assertTrue(ret) self.assertTrue(isinstance(ret, self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/1/".format( self.app, self.name.replace("_", "-") ), params={}, json=None, headers=HEADERS, ) class TenantsTestCase(Generic.Tests): name = "tenants" class TenantGroupsTestCase(Generic.Tests): name = "tenant_groups" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/test_users.py0000644000175100017510000001016115136437134016765 0ustar00runnerrunnerimport unittest from unittest.mock import patch import pynetbox from .util import Response api = pynetbox.api( "http://localhost:8000", ) nb = api.users HEADERS = {"accept": "application/json"} class Generic: class Tests(unittest.TestCase): name = "" ret = pynetbox.core.response.Record app = "users" def test_get_all(self): with patch( "requests.sessions.Session.get", return_value=Response(fixture="{}/{}.json".format(self.app, self.name)), ) as mock: ret = list(getattr(nb, self.name).all()) self.assertTrue(ret) self.assertTrue(isinstance(ret[0], self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/".format( self.app, self.name.replace("_", "-") ), params={"limit": 0}, json=None, headers=HEADERS, ) def test_filter(self): with patch( "requests.sessions.Session.get", return_value=Response(fixture="{}/{}.json".format(self.app, self.name)), ) as mock: ret = list(getattr(nb, self.name).filter(name="test")) self.assertTrue(ret) self.assertTrue(isinstance(ret[0], self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/".format( self.app, self.name.replace("_", "-") ), params={"name": "test", "limit": 0}, json=None, headers=HEADERS, ) def test_get(self): with patch( "requests.sessions.Session.get", return_value=Response( fixture="{}/{}.json".format(self.app, self.name[:-1]) ), ) as mock: ret = getattr(nb, self.name).get(1) self.assertTrue(ret) self.assertTrue(isinstance(ret, self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/1/".format( self.app, self.name.replace("_", "-") ), params={}, json=None, headers=HEADERS, ) class UsersTestCase(Generic.Tests): name = "users" @patch( "requests.sessions.Session.get", return_value=Response(fixture="users/user.json"), ) def test_repr(self, _): test = nb.users.get(1) self.assertEqual(type(test), pynetbox.models.users.Users) self.assertEqual(str(test), "user1") class GroupsTestCase(Generic.Tests): name = "groups" class PermissionsTestCase(Generic.Tests): name = "permissions" @patch( "requests.sessions.Session.get", return_value=Response(fixture="users/permission.json"), ) def test_username(self, _): permission = nb.permissions.get(1) self.assertEqual(len(permission.users), 1) user = permission.users[0] self.assertEqual(str(user), "user1") @patch( "requests.sessions.Session.get", return_value=Response(fixture="users/permission.json"), ) def test_constraints(self, _): permission = nb.permissions.get(1) self.assertIsInstance(permission.constraints, list) self.assertIsInstance(permission.constraints[0], dict) class UnknownModelTestCase(unittest.TestCase): """This test validates that an unknown model is returned as Record object and that the __str__() method correctly uses the 'display' field of the object (introduced as a standard field in NetBox 2.11.0). """ @patch( "requests.sessions.Session.get", return_value=Response(fixture="users/unknown_model.json"), ) def test_unknown_model(self, _): unknown_obj = nb.unknown_model.get(1) self.assertEqual(type(unknown_obj), pynetbox.core.response.Record) self.assertEqual(str(unknown_obj), "Unknown object") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/test_virtualization.py0000644000175100017510000000542215136437134020714 0ustar00runnerrunnerimport unittest from unittest.mock import patch import pynetbox from .util import Response api = pynetbox.api( "http://localhost:8000", ) nb = api.virtualization HEADERS = {"accept": "application/json"} class Generic: class Tests(unittest.TestCase): name = "" ret = pynetbox.core.response.Record app = "virtualization" def test_get_all(self): with patch( "requests.sessions.Session.get", return_value=Response(fixture="{}/{}.json".format(self.app, self.name)), ) as mock: ret = list(getattr(nb, self.name).all()) self.assertTrue(ret) self.assertTrue(isinstance(ret[0], self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/".format( self.app, self.name.replace("_", "-") ), params={"limit": 0}, json=None, headers=HEADERS, ) def test_filter(self): with patch( "requests.sessions.Session.get", return_value=Response(fixture="{}/{}.json".format(self.app, self.name)), ) as mock: ret = list(getattr(nb, self.name).filter(name="test")) self.assertTrue(ret) self.assertTrue(isinstance(ret[0], self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/".format( self.app, self.name.replace("_", "-") ), params={"name": "test", "limit": 0}, json=None, headers=HEADERS, ) def test_get(self): with patch( "requests.sessions.Session.get", return_value=Response( fixture="{}/{}.json".format(self.app, self.name[:-1]) ), ) as mock: ret = getattr(nb, self.name).get(1) self.assertTrue(ret) self.assertTrue(isinstance(ret, self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/1/".format( self.app, self.name.replace("_", "-") ), params={}, json=None, headers=HEADERS, ) class ClusterTypesTestCase(Generic.Tests): name = "cluster_types" class ClusterGroupsTestCase(Generic.Tests): name = "cluster_groups" class ClustersTestCase(Generic.Tests): name = "clusters" class VirtualMachinesTestCase(Generic.Tests): name = "virtual_machines" class InterfacesTestCase(Generic.Tests): name = "interfaces" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/test_wireless.py0000644000175100017510000000553315136437134017470 0ustar00runnerrunnerimport unittest from unittest.mock import patch import pynetbox from .util import Response api = pynetbox.api("http://localhost:8000") nb_app = api.wireless HEADERS = {"accept": "application/json"} class Generic: class Tests(unittest.TestCase): name = "" ret = pynetbox.core.response.Record app = "wireless" def test_get_all(self): with patch( "requests.sessions.Session.get", return_value=Response(fixture="{}/{}.json".format(self.app, self.name)), ) as mock: ret = list(getattr(nb_app, self.name).all()) self.assertTrue(ret) self.assertTrue(isinstance(ret[0], self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/".format( self.app, self.name.replace("_", "-") ), params={"limit": 0}, json=None, headers=HEADERS, ) def test_filter(self): with patch( "requests.sessions.Session.get", return_value=Response(fixture="{}/{}.json".format(self.app, self.name)), ) as mock: ret = list(getattr(nb_app, self.name).filter(name="test")) self.assertTrue(ret) self.assertTrue(isinstance(ret[0], self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/".format( self.app, self.name.replace("_", "-") ), params={"name": "test", "limit": 0}, json=None, headers=HEADERS, ) def test_get(self): with patch( "requests.sessions.Session.get", return_value=Response( fixture="{}/{}.json".format(self.app, self.name[:-1]) ), ) as mock: ret = getattr(nb_app, self.name).get(1) self.assertTrue(ret) self.assertTrue(isinstance(ret, self.ret)) mock.assert_called_with( "http://localhost:8000/api/{}/{}/1/".format( self.app, self.name.replace("_", "-") ), params={}, json=None, headers=HEADERS, ) class WirelessLansTestCase(Generic.Tests): name = "wireless_lans" @patch( "requests.sessions.Session.get", return_value=Response(fixture="wireless/wireless_lan.json"), ) def test_repr(self, _): wireless_lan_obj = nb_app.wireless_lans.get(1) self.assertEqual(type(wireless_lan_obj), pynetbox.models.wireless.WirelessLans) self.assertEqual(str(wireless_lan_obj), "SSID 1") ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1769619043.8223305 pynetbox-7.6.1/tests/unit/0000755000175100017510000000000015136437144015174 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/unit/__init__.py0000644000175100017510000000000015136437134017272 0ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/unit/test_detailendpoint.py0000644000175100017510000000505315136437134021612 0ustar00runnerrunnerimport unittest from unittest.mock import patch import pynetbox nb = pynetbox.api("http://localhost:8000") class DetailEndpointTestCase(unittest.TestCase): def test_detail_endpoint_create_single(self): # Prefixes with patch( "pynetbox.core.query.Request._make_call", return_value={"id": 123, "prefix": "1.2.3.0/24"}, ): prefix_obj = nb.ipam.prefixes.get(123) self.assertEqual(prefix_obj.prefix, "1.2.3.0/24") with patch( "pynetbox.core.query.Request._make_call", return_value={"address": "1.2.3.1/24"}, ): ip_obj = prefix_obj.available_ips.create() self.assertEqual(ip_obj.address, "1.2.3.1/24") # IP Ranges with patch( "pynetbox.core.query.Request._make_call", return_value={"id": 321, "display": "1.2.4.1-254/24"}, ): ip_range_obj = nb.ipam.ip_ranges.get(321) self.assertEqual(ip_range_obj.display, "1.2.4.1-254/24") with patch( "pynetbox.core.query.Request._make_call", return_value={"address": "1.2.4.2/24"}, ): ip_obj = ip_range_obj.available_ips.create() self.assertEqual(ip_obj.address, "1.2.4.2/24") def test_detail_endpoint_create_list(self): # Prefixes with patch( "pynetbox.core.query.Request._make_call", return_value={"id": 123, "prefix": "1.2.3.0/24"}, ): prefix_obj = nb.ipam.prefixes.get(123) self.assertEqual(prefix_obj.prefix, "1.2.3.0/24") with patch( "pynetbox.core.query.Request._make_call", return_value=[{"address": "1.2.3.1/24"}, {"address": "1.2.3.2/24"}], ): ip_list = prefix_obj.available_ips.create([{} for _ in range(2)]) self.assertTrue(isinstance(ip_list, list)) self.assertEqual(len(ip_list), 2) # IP Ranges with patch( "pynetbox.core.query.Request._make_call", return_value={"id": 321, "display": "1.2.4.1-254/24"}, ): ip_range_obj = nb.ipam.ip_ranges.get(321) self.assertEqual(ip_range_obj.display, "1.2.4.1-254/24") with patch( "pynetbox.core.query.Request._make_call", return_value=[{"address": "1.2.4.2/24"}, {"address": "1.2.4.3/24"}], ): ip_list = ip_range_obj.available_ips.create([{} for _ in range(2)]) self.assertTrue(isinstance(ip_list, list)) self.assertEqual(len(ip_list), 2) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/unit/test_endpoint.py0000644000175100017510000002267615136437134020441 0ustar00runnerrunnerimport unittest from unittest.mock import Mock, patch from pynetbox.core.endpoint import Endpoint class EndPointTestCase(unittest.TestCase): def test_filter(self): with patch( "pynetbox.core.query.Request._make_call", return_value=Mock() ) as mock: api = Mock(base_url="http://localhost:8000/api", strict_filters=False) app = Mock(name="test") mock.return_value = [{"id": 123}, {"id": 321}] test_obj = Endpoint(api, app, "test") test = test_obj.filter(test="test") self.assertEqual(len(test), 2) def test_filter_invalid_pagination_args(self): api = Mock(base_url="http://localhost:8000/api") app = Mock(name="test") test_obj = Endpoint(api, app, "test") with self.assertRaises(ValueError) as _: test_obj.filter(offset=1) def test_filter_replace_none_with_null(self): api = Mock(base_url="http://localhost:8000/api", strict_filters=False) app = Mock(name="test") test_obj = Endpoint(api, app, "test") test = test_obj.filter(name=None, id=0) self.assertEqual(test.request.filters, {"name": "null", "id": 0}) def test_all_invalid_pagination_args(self): api = Mock(base_url="http://localhost:8000/api") app = Mock(name="test") test_obj = Endpoint(api, app, "test") with self.assertRaises(ValueError) as _: test_obj.all(offset=1) def test_choices(self): with patch("pynetbox.core.query.Request.options", return_value=Mock()) as mock: api = Mock(base_url="http://localhost:8000/api") app = Mock(name="test") mock.return_value = { "actions": { "POST": { "letter": { "choices": [ {"display_name": "A", "value": 1}, {"display_name": "B", "value": 2}, {"display_name": "C", "value": 3}, ] } } } } test_obj = Endpoint(api, app, "test") choices = test_obj.choices() self.assertEqual(choices["letter"][1]["display_name"], "B") self.assertEqual(choices["letter"][1]["value"], 2) def test_choices_put(self): with patch("pynetbox.core.query.Request.options", return_value=Mock()) as mock: api = Mock(base_url="http://localhost:8000/api") app = Mock(name="test") mock.return_value = { "actions": { "PUT": { "letter": { "choices": [ {"display_name": "A", "value": 1}, {"display_name": "B", "value": 2}, {"display_name": "C", "value": 3}, ] } } } } test_obj = Endpoint(api, app, "test") choices = test_obj.choices() self.assertEqual(choices["letter"][0]["display_name"], "A") self.assertEqual(choices["letter"][0]["value"], 1) def test_choices_precedence(self): with patch("pynetbox.core.query.Request.options", return_value=Mock()) as mock: api = Mock(base_url="http://localhost:8000/api") app = Mock(name="test") mock.return_value = { "actions": { "POST": { "letter": { "choices": [ {"display_name": "A", "value": 1}, {"display_name": "B", "value": 2}, {"display_name": "C", "value": 3}, ] } }, "PUT": { "letter": { "choices": [ {"display_name": "D", "value": 4}, {"display_name": "E", "value": 5}, {"display_name": "F", "value": 6}, ] } }, } } test_obj = Endpoint(api, app, "test") choices = test_obj.choices() self.assertEqual(choices["letter"][2]["display_name"], "C") self.assertEqual(choices["letter"][2]["value"], 3) def test_get_with_filter(self): with patch( "pynetbox.core.query.Request._make_call", return_value=Mock() ) as mock: mock.return_value = [{"id": 123}] api = Mock(base_url="http://localhost:8000/api", strict_filters=False) app = Mock(name="test") test_obj = Endpoint(api, app, "test") test = test_obj.get(name="test") self.assertEqual(test.id, 123) def test_delete_with_ids(self): with patch( "pynetbox.core.query.Request._make_call", return_value=Mock() ) as mock: ids = [1, 3, 5] mock.return_value = True api = Mock(base_url="http://localhost:8000/api") app = Mock(name="test") test_obj = Endpoint(api, app, "test") test = test_obj.delete(ids) mock.assert_called_with(verb="delete", data=[{"id": i} for i in ids]) self.assertTrue(test) def test_delete_with_objects(self): with patch( "pynetbox.core.query.Request._make_call", return_value=Mock() ) as mock: from pynetbox.core.response import Record ids = [1, 3, 5] mock.return_value = True api = Mock(base_url="http://localhost:8000/api") app = Mock(name="test") test_obj = Endpoint(api, app, "test") objects = [ Record({"id": i, "name": "dummy" + str(i)}, api, test_obj) for i in ids ] test = test_obj.delete(objects) mock.assert_called_with(verb="delete", data=[{"id": i} for i in ids]) self.assertTrue(test) def test_delete_with_recordset(self): with patch( "pynetbox.core.query.Request._make_call", return_value=Mock() ) as mock: from pynetbox.core.response import RecordSet ids = [1, 3, 5] class FakeRequest: def get(self): return iter([{"id": i, "name": "dummy" + str(i)} for i in ids]) mock.return_value = True api = Mock(base_url="http://localhost:8000/api") app = Mock(name="test") test_obj = Endpoint(api, app, "test") recordset = RecordSet(test_obj, FakeRequest()) test = test_obj.delete(recordset) mock.assert_called_with(verb="delete", data=[{"id": i} for i in ids]) self.assertTrue(test) def test_get_greater_than_one(self): with patch( "pynetbox.core.query.Request._make_call", return_value=Mock() ) as mock: mock.return_value = [{"id": 123}, {"id": 321}] api = Mock(base_url="http://localhost:8000/api", strict_filters=False) app = Mock(name="test") test_obj = Endpoint(api, app, "test") with self.assertRaises(ValueError) as _: test_obj.get(name="test") def test_get_no_results(self): with patch( "pynetbox.core.query.Request._make_call", return_value=Mock() ) as mock: mock.return_value = [] api = Mock(base_url="http://localhost:8000/api", strict_filters=False) app = Mock(name="test") test_obj = Endpoint(api, app, "test") test = test_obj.get(name="test") self.assertIsNone(test) def test_bulk_update_records(self): with patch( "pynetbox.core.query.Request._make_call", return_value=Mock() ) as mock: from pynetbox.core.response import Record ids = [1, 3, 5] mock.return_value = True api = Mock(base_url="http://localhost:8000/api") app = Mock(name="test") test_obj = Endpoint(api, app, "test") objects = [ Record( {"id": i, "name": "dummy" + str(i), "unchanged": "yes"}, api, test_obj, ) for i in ids ] for o in objects: o.name = "fluffy" + str(o.id) mock.return_value = [o.serialize() for o in objects] test = test_obj.update(objects) mock.assert_called_with( verb="patch", data=[{"id": i, "name": "fluffy" + str(i)} for i in ids] ) self.assertTrue(test) def test_bulk_update_json(self): with patch( "pynetbox.core.query.Request._make_call", return_value=Mock() ) as mock: ids = [1, 3, 5] changes = [{"id": i, "name": "puffy" + str(i)} for i in ids] mock.return_value = True api = Mock(base_url="http://localhost:8000/api") app = Mock(name="test") mock.return_value = changes test_obj = Endpoint(api, app, "test") test = test_obj.update(changes) mock.assert_called_with(verb="patch", data=changes) self.assertTrue(test) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/unit/test_endpoint_strict_filter.py0000644000175100017510000000370515136437134023366 0ustar00runnerrunnerimport unittest from unittest.mock import Mock from tests.util import openapi_mock from pynetbox.core.endpoint import Endpoint from pynetbox import ParameterValidationError class StrictFilterTestCase(unittest.TestCase): def test_filter_strict_valid_kwargs(self): api = Mock( base_url="http://localhost:8000/api", strict_filters=True, openapi=openapi_mock, ) app = Mock(name="test") app.name = "test" test_obj = Endpoint(api, app, "test") test_obj.filter(test="test") def test_filter_strict_invalid_kwarg(self): api = Mock( base_url="http://localhost:8000/api", strict_filters=True, openapi=openapi_mock, ) app = Mock(name="test") app.name = "test" test_obj = Endpoint(api, app, "test") with self.assertRaises(ParameterValidationError): test_obj.filter(very_invalid_kwarg="test") def test_filter_strict_per_request_disable_invalid_kwarg(self): api = Mock( base_url="http://localhost:8000/api", strict_filters=True, # Enable globally openapi=openapi_mock, ) app = Mock(name="test") app.name = "test" test_obj = Endpoint(api, app, "test") # Disable strict_filters only for this specific request test_obj.filter(very_invalid_kwarg="test", strict_filters=False) def test_filter_strict_per_request_enable_invalid_kwarg(self): api = Mock( base_url="http://localhost:8000/api", strict_filters=False, # Disable globally openapi=openapi_mock, ) app = Mock(name="test") app.name = "test" test_obj = Endpoint(api, app, "test") with self.assertRaises(ParameterValidationError): # Enable strict_filters only for this specific request test_obj.filter(very_invalid_kwarg="test", strict_filters=True) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/unit/test_extras.py0000644000175100017510000000235415136437134020116 0ustar00runnerrunnerimport unittest from pynetbox.models.extras import ConfigContexts class ExtrasTestCase(unittest.TestCase): def test_config_contexts(self): test_values = { "data": { "test_int": 123, "test_str": "testing", "test_list": [1, 2, 3], } } test = ConfigContexts(test_values, None, None) self.assertTrue(test) def test_config_contexts_diff_str(self): test_values = { "data": { "test_int": 123, "test_str": "testing", "test_list": [1, 2, 3], "test_dict": {"foo": "bar"}, } } test = ConfigContexts(test_values, None, None) test.data["test_str"] = "bar" self.assertEqual(test._diff(), {"data"}) def test_config_contexts_diff_dict(self): test_values = { "data": { "test_int": 123, "test_str": "testing", "test_list": [1, 2, 3], "test_dict": {"foo": "bar"}, } } test = ConfigContexts(test_values, None, None) test.data["test_dict"].update({"bar": "foo"}) self.assertEqual(test._diff(), {"data"}) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/unit/test_file_upload.py0000644000175100017510000002103715136437134021072 0ustar00runnerrunner"""Tests for file upload/multipart support.""" import io import unittest from unittest.mock import Mock, patch from pynetbox.core.query import Request, _extract_files, _is_file_like from pynetbox.core.endpoint import Endpoint class TestIsFileLike(unittest.TestCase): """Tests for _is_file_like helper function.""" def test_file_object(self): """File objects should be detected as file-like.""" f = io.BytesIO(b"test content") self.assertTrue(_is_file_like(f)) def test_string_io(self): """StringIO objects should be detected as file-like.""" f = io.StringIO("test content") self.assertTrue(_is_file_like(f)) def test_string(self): """Strings should not be detected as file-like.""" self.assertFalse(_is_file_like("test")) def test_bytes(self): """Bytes should not be detected as file-like.""" self.assertFalse(_is_file_like(b"test")) def test_dict(self): """Dicts should not be detected as file-like.""" self.assertFalse(_is_file_like({"key": "value"})) def test_none(self): """None should not be detected as file-like.""" self.assertFalse(_is_file_like(None)) def test_non_callable_read_attribute(self): """Objects with non-callable read attribute should not be file-like.""" class FakeFile: read = "not a method" self.assertFalse(_is_file_like(FakeFile())) class TestExtractFiles(unittest.TestCase): """Tests for _extract_files helper function.""" def test_no_files(self): """Data without files should return unchanged.""" data = {"name": "test", "device": 1} clean_data, files = _extract_files(data) self.assertEqual(clean_data, {"name": "test", "device": 1}) self.assertIsNone(files) def test_extract_file_object(self): """File objects should be extracted from data.""" file_obj = io.BytesIO(b"test content") data = {"name": "test", "image": file_obj} clean_data, files = _extract_files(data) self.assertEqual(clean_data, {"name": "test"}) self.assertIn("image", files) self.assertEqual(files["image"][0], "image") # filename defaults to key self.assertEqual(files["image"][1], file_obj) def test_extract_file_with_name_attribute(self): """File objects with name attribute should use that as filename.""" file_obj = io.BytesIO(b"test content") file_obj.name = "/path/to/image.png" data = {"name": "test", "image": file_obj} clean_data, files = _extract_files(data) self.assertEqual(files["image"][0], "image.png") # basename extracted def test_tuple_format(self): """Files passed as tuples should be preserved.""" file_obj = io.BytesIO(b"test content") data = {"name": "test", "image": ("custom_name.png", file_obj, "image/png")} clean_data, files = _extract_files(data) self.assertEqual(clean_data, {"name": "test"}) self.assertEqual(files["image"], ("custom_name.png", file_obj, "image/png")) def test_non_dict_data(self): """Non-dict data should be returned unchanged.""" data = [{"id": 1}, {"id": 2}] result_data, files = _extract_files(data) self.assertEqual(result_data, data) self.assertIsNone(files) class TestRequestWithFiles(unittest.TestCase): """Tests for Request._make_call with file uploads.""" def test_post_with_files_uses_multipart(self): """POST with files should use multipart/form-data.""" mock_session = Mock() mock_session.post.return_value.ok = True mock_session.post.return_value.status_code = 201 mock_session.post.return_value.json.return_value = {"id": 1, "name": "test"} file_obj = io.BytesIO(b"test content") req = Request( base="http://localhost:8000/api/extras/image-attachments", http_session=mock_session, token="testtoken", ) req._make_call( verb="post", data={"object_type": "dcim.device", "object_id": 1, "image": file_obj}, ) # Verify multipart was used (data= instead of json=) mock_session.post.assert_called_once() call_kwargs = mock_session.post.call_args self.assertIn("data", call_kwargs.kwargs) self.assertIn("files", call_kwargs.kwargs) self.assertNotIn("json", call_kwargs.kwargs) # Verify Content-Type was not set (requests handles it for multipart) headers = call_kwargs.kwargs["headers"] self.assertNotIn("Content-Type", headers) self.assertEqual(headers["accept"], "application/json") self.assertEqual(headers["authorization"], "Token testtoken") def test_post_without_files_uses_json(self): """POST without files should use JSON.""" mock_session = Mock() mock_session.post.return_value.ok = True mock_session.post.return_value.status_code = 201 mock_session.post.return_value.json.return_value = {"id": 1, "name": "test"} req = Request( base="http://localhost:8000/api/dcim/devices", http_session=mock_session, token="testtoken", ) req._make_call(verb="post", data={"name": "test-device", "site": 1}) mock_session.post.assert_called_once() call_kwargs = mock_session.post.call_args self.assertIn("json", call_kwargs.kwargs) self.assertNotIn("files", call_kwargs.kwargs) self.assertEqual( call_kwargs.kwargs["headers"]["Content-Type"], "application/json" ) def test_patch_with_files_uses_multipart(self): """PATCH with files should use multipart/form-data.""" mock_session = Mock() mock_session.patch.return_value.ok = True mock_session.patch.return_value.status_code = 200 mock_session.patch.return_value.json.return_value = {"id": 1, "name": "test"} file_obj = io.BytesIO(b"new content") req = Request( base="http://localhost:8000/api/extras/image-attachments", http_session=mock_session, token="testtoken", key=1, ) req._make_call(verb="patch", data={"image": file_obj}) mock_session.patch.assert_called_once() call_kwargs = mock_session.patch.call_args self.assertIn("data", call_kwargs.kwargs) self.assertIn("files", call_kwargs.kwargs) def test_patch_without_files_uses_json(self): """PATCH without files should use JSON and set Content-Type.""" mock_session = Mock() mock_session.patch.return_value.ok = True mock_session.patch.return_value.status_code = 200 mock_session.patch.return_value.json.return_value = {"id": 1, "name": "updated"} req = Request( base="http://localhost:8000/api/dcim/devices", http_session=mock_session, token="testtoken", key=1, ) req._make_call(verb="patch", data={"name": "updated-device"}) mock_session.patch.assert_called_once() call_kwargs = mock_session.patch.call_args self.assertIn("json", call_kwargs.kwargs) self.assertNotIn("files", call_kwargs.kwargs) self.assertEqual( call_kwargs.kwargs["headers"]["Content-Type"], "application/json" ) class TestEndpointCreateWithFiles(unittest.TestCase): """Tests for Endpoint.create() with file uploads.""" def test_create_image_attachment(self): """Creating image attachment should work with file objects.""" with patch("pynetbox.core.query.Request._make_call") as mock_call: mock_call.return_value = { "id": 1, "object_type": "dcim.device", "object_id": 1, "image": "/media/image-attachments/test.png", "name": "test.png", } api = Mock(base_url="http://localhost:8000/api") app = Mock(name="extras") endpoint = Endpoint(api, app, "image_attachments") file_obj = io.BytesIO(b"fake image content") endpoint.create( object_type="dcim.device", object_id=1, image=file_obj, name="test.png" ) mock_call.assert_called_once() call_kwargs = mock_call.call_args self.assertEqual(call_kwargs.kwargs["verb"], "post") data = call_kwargs.kwargs["data"] self.assertEqual(data["object_type"], "dcim.device") self.assertEqual(data["object_id"], 1) self.assertEqual(data["image"], file_obj) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1769619036.0 pynetbox-7.6.1/tests/unit/test_multiformat_endpoint.py0000644000175100017510000001210715136437134023050 0ustar00runnerrunner"""Tests for ROMultiFormatDetailEndpoint.""" import unittest from unittest.mock import patch import pynetbox class ROMultiFormatDetailEndpointTestCase(unittest.TestCase): """Test cases for ROMultiFormatDetailEndpoint class.""" def setUp(self): """Set up test fixtures.""" self.nb = pynetbox.api("http://localhost:8000", token="test-token") def test_list_returns_json_by_default(self): """list() without render parameter returns JSON data.""" with patch( "pynetbox.core.query.Request._make_call", return_value={"id": 123, "name": "Test Rack"}, ): rack = self.nb.dcim.racks.get(123) with patch( "pynetbox.core.query.Request._make_call", return_value=[ {"id": 1, "name": "U1"}, {"id": 2, "name": "U2"}, ], ): result = rack.elevation.list() # Should return generator that yields dict items result_list = list(result) self.assertEqual(len(result_list), 2) # Verify custom_return was applied (RUs objects) self.assertTrue(hasattr(result_list[0], "id")) def test_list_with_render_svg_returns_raw_string(self): """list(render='svg') returns raw SVG string.""" with patch( "pynetbox.core.query.Request._make_call", return_value={"id": 123, "name": "Test Rack"}, ): rack = self.nb.dcim.racks.get(123) svg_content = '' with patch( "pynetbox.core.query.Request._make_call", return_value=svg_content, ): result = rack.elevation.list(render="svg") # Should return raw string, not wrapped in list self.assertIsInstance(result, str) self.assertEqual(result, svg_content) self.assertIn("