├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── docs ├── Makefile ├── authentication.rst ├── conf.py ├── examples.rst ├── index.rst ├── installation.rst └── source │ ├── modules.rst │ └── netbox.rst ├── netbox ├── __init__.py ├── circuits.py ├── connection.py ├── dcim.py ├── exceptions.py ├── extras.py ├── ipam.py ├── netbox.py ├── status.py ├── tenancy.py └── virtualization.py ├── requirements.txt ├── setup.py └── test_requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.diff 3 | *.err 4 | *.orig 5 | *.log 6 | *.rej 7 | *.swo 8 | *.swp 9 | *.vi 10 | *.cache 11 | *.egg-info 12 | *~ 13 | *# 14 | 15 | # OS or Editor folders 16 | .DS_Store 17 | Thumbs.db 18 | .cache 19 | .project 20 | .settings 21 | ._* 22 | .CVS 23 | .idea 24 | _assets 25 | _design 26 | _content 27 | _tmp 28 | dist 29 | build 30 | _build 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagter/python-netbox/845921cae64eafe23bad57021fc2833b24be32f9/MANIFEST.in -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Python Netbox Client 3 | ============================ 4 | 5 | python-netbox is a client for the Netbox (https://github.com/digitalocean/netbox) API. 6 | It's based on the APIv2 which is released since version 2.0.0. It requires python 3. 7 | 8 | The full documentation can be found here_. 9 | 10 | .. _here: http://python-netbox.readthedocs.io/en/latest/# 11 | 12 | ----------------- 13 | Installation 14 | ----------------- 15 | 16 | To get the latest version from Github: 17 | 18 | $ pip3 install python-netbox 19 | 20 | ----------------- 21 | Usage 22 | ----------------- 23 | To start with the module: 24 | 25 | >>> from netbox import NetBox 26 | >>> netbox = NetBox(host='127.0.0.1', port=32768, use_ssl=False, auth_token='token') 27 | 28 | 29 | ----------------- 30 | Examples 31 | ----------------- 32 | Get all devices: 33 | 34 | >>> netbox.dcim.get_devices() 35 | 36 | Get devices per rack: 37 | 38 | >>> netbox.dcim.get_devices(rack_id=1) 39 | 40 | Get device by name 41 | 42 | >>> netbox.dcim.get_devices(name='device_name') 43 | 44 | Create a site: 45 | 46 | >>> netbox.dcim.create_site('site1', 'site1') 47 | 48 | Delete a site: 49 | 50 | >>> netbox.dcim.delete_site('site1') 51 | 52 | Get IP address object: 53 | 54 | >>> netbox.ipam.get_ip_addresses(address='10.0.0.1/16') 55 | 56 | ----------------- 57 | Support 58 | ----------------- 59 | If you have questions or comments please send an email to thomas@tjrb.nl 60 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = python-netbox 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/authentication.rst: -------------------------------------------------------------------------------- 1 | ############################################## 2 | Authentication 3 | ############################################## 4 | 5 | By default you can get all the information from Netbox if the login_required option is set to False. If the option is 6 | set to True, you can access the api by username and password to GET the information. The API is only writable if you 7 | have a Token. 8 | 9 | * Use the auth_token parameter for the writeable api 10 | * Use the auth parameter to use username and password. The auth parameter is a tuple ('username','password') 11 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # python-netbox documentation build configuration file, created by 5 | # sphinx-quickstart on Thu Jun 29 14:00:10 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | import os 21 | import sys 22 | sys.path.insert(0, os.path.abspath('..')) 23 | 24 | 25 | # -- General configuration ------------------------------------------------ 26 | 27 | # If your documentation needs a minimal Sphinx version, state it here. 28 | # 29 | # needs_sphinx = '1.0' 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = ['sphinx.ext.autodoc'] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ['_templates'] 38 | 39 | # The suffix(es) of source filenames. 40 | # You can specify multiple suffix as a list of string: 41 | # 42 | # source_suffix = ['.rst', '.md'] 43 | source_suffix = '.rst' 44 | 45 | # The master toctree document. 46 | master_doc = 'index' 47 | 48 | # General information about the project. 49 | project = 'python-netbox' 50 | copyright = '2017, Thomas van der Jagt' 51 | author = 'Thomas van der Jagt' 52 | 53 | # The version info for the project you're documenting, acts as replacement for 54 | # |version| and |release|, also used in various other places throughout the 55 | # built documents. 56 | # 57 | # The short X.Y version. 58 | version = '0.0.1' 59 | # The full version, including alpha/beta/rc tags. 60 | release = '0.0.1' 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | # 65 | # This is also used if you do content translation via gettext catalogs. 66 | # Usually you set "language" from the command line for these cases. 67 | language = None 68 | 69 | # List of patterns, relative to source directory, that match files and 70 | # directories to ignore when looking for source files. 71 | # This patterns also effect to html_static_path and html_extra_path 72 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 73 | 74 | # The name of the Pygments (syntax highlighting) style to use. 75 | pygments_style = 'sphinx' 76 | 77 | # If true, `todo` and `todoList` produce output, else they produce nothing. 78 | todo_include_todos = False 79 | 80 | 81 | # -- Options for HTML output ---------------------------------------------- 82 | 83 | # The theme to use for HTML and HTML Help pages. See the documentation for 84 | # a list of builtin themes. 85 | # 86 | # html_theme = 'sphinx_rtd_theme' 87 | 88 | # Theme options are theme-specific and customize the look and feel of a theme 89 | # further. For a list of options available for each theme, see the 90 | # documentation. 91 | # 92 | # html_theme_options = {} 93 | 94 | # Add any paths that contain custom static files (such as style sheets) here, 95 | # relative to this directory. They are copied after the builtin static files, 96 | # so a file named "default.css" will overwrite the builtin "default.css". 97 | # html_static_path = ['_static'] 98 | 99 | 100 | # -- Options for HTMLHelp output ------------------------------------------ 101 | 102 | # Output file base name for HTML help builder. 103 | htmlhelp_basename = 'python-netboxdoc' 104 | 105 | 106 | # -- Options for LaTeX output --------------------------------------------- 107 | 108 | latex_elements = { 109 | # The paper size ('letterpaper' or 'a4paper'). 110 | # 111 | # 'papersize': 'letterpaper', 112 | 113 | # The font size ('10pt', '11pt' or '12pt'). 114 | # 115 | # 'pointsize': '10pt', 116 | 117 | # Additional stuff for the LaTeX preamble. 118 | # 119 | # 'preamble': '', 120 | 121 | # Latex figure (float) alignment 122 | # 123 | # 'figure_align': 'htbp', 124 | } 125 | 126 | # Grouping the document tree into LaTeX files. List of tuples 127 | # (source start file, target name, title, 128 | # author, documentclass [howto, manual, or own class]). 129 | latex_documents = [ 130 | (master_doc, 'python-netbox.tex', 'python-netbox Documentation', 131 | 'Thomas van der Jagt', 'manual'), 132 | ] 133 | 134 | 135 | # -- Options for manual page output --------------------------------------- 136 | 137 | # One entry per manual page. List of tuples 138 | # (source start file, name, description, authors, manual section). 139 | man_pages = [ 140 | (master_doc, 'python-netbox', 'python-netbox Documentation', 141 | [author], 1) 142 | ] 143 | 144 | 145 | # -- Options for Texinfo output ------------------------------------------- 146 | 147 | # Grouping the document tree into Texinfo files. List of tuples 148 | # (source start file, target name, title, author, 149 | # dir menu entry, description, category) 150 | texinfo_documents = [ 151 | (master_doc, 'python-netbox', 'python-netbox Documentation', 152 | author, 'python-netbox', 'One line description of project.', 153 | 'Miscellaneous'), 154 | ] 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /docs/examples.rst: -------------------------------------------------------------------------------- 1 | ############################################## 2 | Examples 3 | ############################################## 4 | 5 | To start with python-netbox client: 6 | 7 | >>> from netbox import NetBox 8 | >>> netbox = NetBox(host='127.0.0.1', port=32768, use_ssl=False, auth_token='token') 9 | 10 | 11 | Get all devices: 12 | 13 | >>> netbox.dcim.get_devices() 14 | 15 | Get devices per rack: 16 | 17 | >>> netbox.dcim.get_devices(rack_id=1) 18 | 19 | Get device by name 20 | 21 | >>> netbox.dcim.get_devices(name='device_name') 22 | 23 | Get per device the primary ip and mac address: 24 | 25 | >>> output = [] 26 | >>> for item in a.dcim.get_devices(): 27 | >>> device_name = item['name'] 28 | >>> 29 | >>> if item['primary_ip'] is not None: 30 | >>> primary_ip_id = item['primary_ip']['id'] 31 | >>> get_ips = a.ipam.get_ip_by_id(primary_ip_id) 32 | >>> 33 | >>> output.append({'name': device_name, 'ip': get_ips['address'], 'mac': get_ips['interface']['mac_address']}) 34 | >>> else: 35 | >>> print('{} has no primary_ip'.format(item['name'])) 36 | >>> 37 | >>> print(output) 38 | 39 | Create a site: 40 | 41 | >>> netbox.dcim.create_site('site1', 'site1') 42 | 43 | Delete a site: 44 | 45 | >>> netbox.dcim.delete_site('site1') -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. python-netbox documentation master file, created by 2 | sphinx-quickstart on Thu Jun 29 14:00:10 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Python Netbox Client 7 | ========================================= 8 | 9 | python-netbox is a client for the Netbox (https://github.com/digitalocean/netbox) API. It's based on the APIv2 which is released since version 2.0.0. It requires python 3. 10 | 11 | 12 | Contents 13 | ======== 14 | 15 | .. toctree:: 16 | installation 17 | authentication 18 | examples 19 | 20 | Indices and tables 21 | ================== 22 | 23 | * :ref:`genindex` 24 | * :ref:`modindex` 25 | * :ref:`search` 26 | 27 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | ############################################## 2 | Installation 3 | ############################################## 4 | 5 | 6 | To get the latest version from Github:: 7 | 8 | pip install python-netbox 9 | 10 | To install the latest version from the master branch:: 11 | 12 | pip install pip install https://github.com/jagter/python-netbox/archive/master.zip 13 | 14 | -------------------------------------------------------------------------------- /docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | netbox 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | netbox 8 | -------------------------------------------------------------------------------- /docs/source/netbox.rst: -------------------------------------------------------------------------------- 1 | netbox package 2 | ============== 3 | 4 | Submodules 5 | ---------- 6 | 7 | netbox.dcim module 8 | ------------------ 9 | 10 | .. automodule:: netbox.dcim 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | netbox.exceptions module 16 | ------------------------ 17 | 18 | .. automodule:: netbox.exceptions 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | netbox.extras module 24 | -------------------- 25 | 26 | .. automodule:: netbox.extras 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | netbox.ipam module 32 | ------------------ 33 | 34 | .. automodule:: netbox.ipam 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | netbox.tenancy module 40 | --------------------- 41 | 42 | .. automodule:: netbox.tenancy 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | netbox.virtualization module 48 | ---------------------------- 49 | 50 | .. automodule:: netbox.virtualization 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | 56 | Module contents 57 | --------------- 58 | 59 | .. automodule:: netbox 60 | :members: 61 | :undoc-members: 62 | :show-inheritance: 63 | -------------------------------------------------------------------------------- /netbox/__init__.py: -------------------------------------------------------------------------------- 1 | from .netbox import NetBox 2 | -------------------------------------------------------------------------------- /netbox/circuits.py: -------------------------------------------------------------------------------- 1 | import netbox.exceptions as exceptions 2 | from netbox.dcim import Dcim 3 | 4 | 5 | class Circuits(object): 6 | 7 | def __init__(self, netbox_con): 8 | self.netbox_con = netbox_con 9 | self.dcim = Dcim(self.netbox_con) 10 | 11 | def get_circuits(self, **kwargs): 12 | """Returns the circuits""" 13 | return self.netbox_con.get('/circuits/circuits/', **kwargs) 14 | 15 | def create_circuit(self, circuit_provider, cid, circuit_type, status_id, **kwargs): 16 | """Create a new circuits 17 | 18 | :param circuit_provider: provider name 19 | :param cid: Unique circuit id 20 | :param circuit_type: circuit type 21 | :param status_id: see below for the status codes 22 | :param kwargs: optional fields 23 | :return: netbox object if successful otherwise exception raised 24 | 25 | circuit status codes: 26 | 0: Deprovisioning 27 | 1: Active 28 | 2: Planned 29 | 3: Provisioning 30 | 4: Offline 31 | 5: Decommissioned 32 | 33 | """ 34 | try: 35 | provider_id = self.get_providers(name=circuit_provider)[0]['id'] 36 | except IndexError: 37 | raise exceptions.NotFoundException({"detail": "cirtcuit provider: {}".format(circuit_provider)}) from None 38 | 39 | try: 40 | type_id = self.get_types(name=circuit_type)[0]['id'] 41 | except IndexError: 42 | raise exceptions.NotFoundException({"detail": "circuit type: {}".format(circuit_type)}) from None 43 | 44 | required_fields = {"provider": provider_id, "circuit": cid, "type": type_id, 45 | "status": status_id} 46 | return self.netbox_con.post('/circuits/circuits/', required_fields, **kwargs) 47 | 48 | def delete_circuit(self, cid, provider): 49 | """Delete circuits 50 | 51 | :param cid: circuit 52 | :param provider: Name of the provider 53 | :return: bool True if successful otherwise delete exception 54 | """ 55 | try: 56 | circuits_id = self.get_circuits(cid=cid, provider=provider)[0]['id'] 57 | except IndexError: 58 | raise exceptions.NotFoundException({"detail": "Circuit with circuit: {} and provider: {}".format(cid, provider)}) from None 59 | return self.netbox_con.delete('/circuits/circuits/', circuits_id) 60 | 61 | def update_circuit(self, cid, **kwargs): 62 | """Update circuit 63 | 64 | :param cid: circuit 65 | :param kwargs: requests body dict 66 | :return: bool True if successful otherwise raise UpdateException 67 | """ 68 | try: 69 | circuit_id = self.get_circuits(cid=cid)[0]['id'] 70 | except IndexError: 71 | raise exceptions.NotFoundException({"detail": "Circuit with circuit: {}".format(cid)}) from None 72 | return self.netbox_con.patch('/circuits/circuits/', circuit_id, **kwargs) 73 | 74 | def get_providers(self, **kwargs): 75 | """Returns circuit providers""" 76 | return self.netbox_con.get('/circuits/providers/', **kwargs) 77 | 78 | def create_provider(self, name, slug): 79 | """Create a new circuit provider 80 | 81 | :param name: provider name 82 | :param slug: slug name 83 | :return: netbox object if successful otherwise exception raised 84 | """ 85 | required_fields = {"name": name, "slug": slug} 86 | return self.netbox_con.post('/circuits/providers/', required_fields) 87 | 88 | def delete_provider(self, provider_name): 89 | """Delete circuit provider 90 | 91 | :param provider_name: circuit provider to delete 92 | :return: bool True if successful otherwise delete exception 93 | """ 94 | try: 95 | circuits_provider_id = self.get_providers(name=provider_name)[0]['id'] 96 | except IndexError: 97 | raise exceptions.NotFoundException({"detail": "circuit provider: {}".format(provider_name)}) from None 98 | return self.netbox_con.delete('/circuits/providers/', circuits_provider_id) 99 | 100 | def update_provider(self, provider_name, **kwargs): 101 | """Update circuit provider 102 | 103 | :param provider_name: circuits role to update 104 | :param kwargs: requests body dict 105 | :return: bool True if successful otherwise raise UpdateException 106 | """ 107 | try: 108 | circuits_provider_id = self.get_providers(name=provider_name)[0]['id'] 109 | except IndexError: 110 | raise exceptions.NotFoundException({"detail": "circuit provider: {}".format(provider_name)}) from None 111 | return self.netbox_con.patch('/circuits/providers/', circuits_provider_id, **kwargs) 112 | 113 | def get_types(self, **kwargs): 114 | """Returns the circuit types""" 115 | return self.netbox_con.get('/circuits/circuit-types/', **kwargs) 116 | 117 | def create_type(self, name, slug): 118 | """Create a new circuit type 119 | 120 | :param name: type name 121 | :param slug: slug name 122 | :return: netbox object if successful otherwise exception raised 123 | """ 124 | required_fields = {"name": name, "slug": slug} 125 | return self.netbox_con.post('/circuits/circuit-types/', required_fields) 126 | 127 | def delete_type(self, type_name): 128 | """Delete circuit type 129 | 130 | :param type_name: circuit type to delete 131 | :return: bool True if succesful otherwise delete exception 132 | """ 133 | try: 134 | circuits_type_id = self.get_types(name=type_name)[0]['id'] 135 | except IndexError: 136 | raise exceptions.NotFoundException({"detail": "circuit type: {}".format(type_name)}) from None 137 | return self.netbox_con.delete('/circuits/circuit-types/', circuits_type_id) 138 | 139 | def update_type(self, circuit_type_name, **kwargs): 140 | """Update circuit role 141 | 142 | :param circuit_type_name: circuits type to update 143 | :param kwargs: requests body dict 144 | :return: bool True if successful otherwise raise UpdateException 145 | """ 146 | try: 147 | type_id = self.get_types(name=circuit_type_name)[0]['id'] 148 | except IndexError: 149 | raise exceptions.NotFoundException({"detail": "circuit type: {}".format(circuit_type_name)}) from None 150 | return self.netbox_con.patch('/circuits/circuit-types/', type_id, **kwargs) 151 | 152 | def get_terminations(self, **kwargs): 153 | """Returns the circuits""" 154 | return self.netbox_con.get('/circuits/circuit-terminations/', **kwargs) 155 | 156 | def create_termination(self, circuit, term_side, site, port_speed, **kwargs): 157 | """Create a new circuit termination 158 | 159 | :param circuit: circuit id 160 | :param term_side: term side A or Z 161 | :param site: Site name 162 | :param port_speed: port speed value 163 | :return: netbox object if successful otherwise exception raised 164 | """ 165 | try: 166 | site_id = self.dcim.get_sites(name=site)[0]['id'] 167 | except IndexError: 168 | raise exceptions.NotFoundException({"detail": "site: {}".format(site)}) from None 169 | 170 | required_fields = {"circuit": circuit, "term_side": term_side, "site": site_id, "port_speed": port_speed} 171 | return self.netbox_con.post('/circuits/circuit-terminations/', required_fields, **kwargs) 172 | 173 | def delete_termination(self, circuit, term_side, site, port_speed): 174 | """Delete circuit termination 175 | 176 | :param circuit: circuit id 177 | :param term_side: term side A or Z 178 | :param site: Site name 179 | :param port_speed: port speed value 180 | :return: bool True if successful otherwise delete exception 181 | """ 182 | try: 183 | site_id = self.dcim.get_sites(name=site)[0]['id'] 184 | except IndexError: 185 | raise exceptions.NotFoundException({"detail": "site: {}".format(site)}) from None 186 | 187 | try: 188 | circuit_termination = self.get_terminations(circuit_id=circuit, term_side=term_side, site_id=site_id, 189 | port_speed=port_speed)[0]['id'] 190 | except IndexError: 191 | raise exceptions.NotFoundException({"detail": "circuit termination with given arguments"}) from None 192 | 193 | return self.netbox_con.delete('/circuits/circuit-terminations/', circuit_termination) 194 | 195 | def update_termination(self, circuit, term_side, site, port_speed, **kwargs): 196 | """Update circuit termination 197 | 198 | :param circuit: circuit id 199 | :param term_side: Termination side 200 | :param site: Dcim Site 201 | :param port_speed: Port speed (Kbps). maximum: 2147483647 202 | :param kwargs: requests body dict 203 | :return: bool True if successful otherwise raise UpdateException 204 | """ 205 | try: 206 | site_id = self.dcim.get_sites(name=site)[0]['id'] 207 | except IndexError: 208 | raise exceptions.NotFoundException({"detail": "site: {}".format(site)}) from None 209 | 210 | try: 211 | circuit_termination_id = self.get_terminations(circuit_id=circuit, term_side=term_side, site_id=site_id, 212 | port_speed=port_speed)[0]['id'] 213 | except IndexError: 214 | raise exceptions.NotFoundException({"detail" "circuit termination with given arguments"}) from None 215 | 216 | return self.netbox_con.patch('/circuits/circuit-terminations/', circuit_termination_id, **kwargs) 217 | -------------------------------------------------------------------------------- /netbox/connection.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import urllib.parse 4 | from netbox import exceptions 5 | 6 | 7 | class NetboxConnection(object): 8 | 9 | def __init__(self, ssl_verify=False, use_ssl=True, host=None, auth_token=None, auth=None, 10 | port=None, api_prefix=None, extra_headers=None): 11 | self.use_ssl = use_ssl 12 | self.host = host 13 | self.auth_token = auth_token 14 | self.port = port 15 | self.auth = auth 16 | self.api_prefix = api_prefix 17 | 18 | self.base_url = 'http{s}://{host}{p}{prefix}'.format(s='s' if use_ssl else '', p=':{}'.format(self.port) if self.port else '', host=self.host, prefix='/api' if api_prefix is None else api_prefix) 19 | 20 | self.session = requests.Session() 21 | self.session.verify = ssl_verify 22 | 23 | if auth: 24 | self.session.auth = auth 25 | 26 | if auth_token: 27 | token = 'Token {}'.format(self.auth_token) 28 | self.session.headers.update({'Authorization': token}) 29 | self.session.headers.update({'Accept': 'application/json'}) 30 | self.session.headers.update({'Content-Type': 'application/json'}) 31 | 32 | if auth and auth_token: 33 | raise exceptions.AuthException('Only one authentication method is possible. Please use auth or auth_token') 34 | 35 | if extra_headers: 36 | self.session.headers.update(extra_headers) 37 | 38 | def __request(self, method, params=None, key=None, body=None, url=None): 39 | 40 | if method != 'GET': 41 | if not self.auth_token: 42 | raise exceptions.AuthException('Authentication credentials were not provided') 43 | 44 | if self.auth: 45 | raise exceptions.AuthException('With basic authentication the API is not writable.') 46 | 47 | if url is None: 48 | if key is not None: 49 | url = self.base_url + str(params) + str('{}/'.format(key)) 50 | else: 51 | url = self.base_url + str(params) 52 | 53 | request = requests.Request(method=method, url=url, json=body) 54 | prepared_request = self.session.prepare_request(request) 55 | 56 | try: 57 | response = self.session.send(prepared_request) 58 | except requests.exceptions.ConnectionError: 59 | err_msg = 'Unable to connect to Netbox host: {}'.format(self.host) 60 | raise ConnectionError(err_msg) from None 61 | except requests.exceptions.Timeout: 62 | raise TimeoutError('Connection to Netbox host timed out') from None 63 | except Exception as e: 64 | raise Exception(e) 65 | finally: 66 | self.close() 67 | 68 | if not 200 <= response.status_code < 300: 69 | self.__raise_error(response.status_code, response.content) 70 | 71 | if response.status_code == 204: 72 | return response.content 73 | 74 | if method == "GET" and ("&export=" in url or "?export=" in url): 75 | # return raw result if export template is specified 76 | return {"results": response.text} 77 | 78 | try: 79 | response_data = response.json() 80 | except json.JSONDecodeError: 81 | raise exceptions.ServerException(response.content) from None 82 | 83 | return response_data 84 | 85 | def get(self, param, key=None, limit=0, **kwargs): 86 | 87 | if kwargs: 88 | url = '{}{}?{}&limit={}'.format(self.base_url, param, 89 | '&'.join('{}={}'.format(key, urllib.parse.quote(str(val))) for key, val in kwargs.items()), limit) 90 | elif key: 91 | if '_choices' in param: 92 | url = '{}{}{}/?limit={}'.format(self.base_url, param, key, limit) 93 | else: 94 | url = '{}{}/?q={}&limit={}'.format(self.base_url, param, key, limit) 95 | else: 96 | url = '{}{}?limit={}'.format(self.base_url, param, limit) 97 | 98 | resp_data = self.__request('GET', params=param, key=key, url=url) 99 | 100 | if 'status' in param: 101 | return resp_data 102 | 103 | return resp_data['results'] 104 | 105 | def put(self, params): 106 | 107 | return self.__request('PUT', params) 108 | 109 | def patch(self, params, key, **kwargs): 110 | 111 | body_data = {key: value for (key, value) in kwargs.items()} 112 | resp_data = self.__request('PATCH', params=params, key=key, body=body_data) 113 | 114 | return resp_data 115 | 116 | def post(self, params, required_fields, **kwargs): 117 | 118 | body_data = {key: value for (key, value) in required_fields.items()} 119 | 120 | if kwargs: 121 | body_data.update({key: value for (key, value) in kwargs.items()}) 122 | resp_data = self.__request('POST', params=params, body=body_data) 123 | 124 | return resp_data 125 | 126 | def delete(self, params, del_id): 127 | 128 | del_str = '{}{}'.format(params, del_id) 129 | self.__request('DELETE', del_str) 130 | 131 | return True 132 | 133 | def close(self): 134 | 135 | self.session.close() 136 | 137 | def __raise_error(self, http_status_code, http_response): 138 | """Raise error with detailed information from http request.""" 139 | try: 140 | error_msg = json.loads(http_response) 141 | except json.JSONDecodeError: 142 | error_msg = http_response 143 | 144 | if http_status_code == 404: 145 | raise exceptions.NotFoundException(error_msg) 146 | elif http_status_code == 403: 147 | raise exceptions.AuthorizationException(error_msg) 148 | elif http_status_code == 400: 149 | raise exceptions.ClientException(error_msg) 150 | elif http_status_code == 503: 151 | raise exceptions.ServerException(error_msg) 152 | -------------------------------------------------------------------------------- /netbox/dcim.py: -------------------------------------------------------------------------------- 1 | import netbox.exceptions as exceptions 2 | 3 | 4 | class Dcim(object): 5 | 6 | def __init__(self, netbox_con): 7 | self.netbox_con = netbox_con 8 | 9 | def get_choices(self, choice_id=None): 10 | """Return choices for all fields if choice_id is not defined 11 | 12 | :param choice_id: Optional model:field tuple 13 | """ 14 | return self.netbox_con.get('/dcim/_choices/', choice_id) 15 | 16 | def get_regions(self, **kwargs): 17 | """Returns the available regions""" 18 | return self.netbox_con.get('/dcim/regions/', **kwargs) 19 | 20 | def create_region(self, name, slug, **kwargs): 21 | """Create a new region 22 | 23 | :param name: Region name 24 | :param slug: slug name 25 | :param kwargs: optional fields 26 | :return: netbox object if successful otherwise exception raised 27 | """ 28 | required_fields = {"name": name, "slug": slug} 29 | return self.netbox_con.post('/dcim/regions/', required_fields, **kwargs) 30 | 31 | def delete_region(self, region_name): 32 | """Delete region 33 | 34 | :param region_name: Region to delete 35 | :return: bool True if succesful otherwise raise exception 36 | """ 37 | try: 38 | region_id = self.get_regions(name=region_name)[0]['id'] 39 | except IndexError: 40 | raise exceptions.NotFoundException({"detail": "region: {}".format(region_name)}) from None 41 | return self.netbox_con.delete('/dcim/regions/', region_id) 42 | 43 | def delete_region_by_id(self, region_id): 44 | """Delete region 45 | 46 | :param region_id: Region to delete 47 | :return: bool True if succesful otherwise raise delete exception 48 | """ 49 | return self.netbox_con.delete('/dcim/regions/', region_id) 50 | 51 | def update_region(self, region_name, **kwargs): 52 | """ 53 | 54 | :param region_name: Region to update 55 | :param kwargs: requests body dict 56 | :return: bool True if successful otherwise raise UpdateException 57 | """ 58 | try: 59 | region_id = self.get_regions(name=region_name)[0]['id'] 60 | except IndexError: 61 | raise exceptions.NotFoundException({"detail": "region: {}".format(region_name)}) from None 62 | return self.netbox_con.patch('/dcim/regions/', region_id, **kwargs) 63 | 64 | def update_region_id(self, region_id, **kwargs): 65 | """Update Region by id 66 | 67 | :param region_id: Region to update 68 | :param kwargs: requests body dict 69 | :return: bool True if successful otherwise raise Exception 70 | """ 71 | return self.netbox_con.patch('/dcim/regions/', region_id, **kwargs) 72 | 73 | def get_sites(self, **kwargs): 74 | """Returns all available sites""" 75 | return self.netbox_con.get('/dcim/sites/', **kwargs) 76 | 77 | def create_site(self, name, slug, **kwargs): 78 | """Create a new site 79 | 80 | :param name: Site name 81 | :param slug: slug name 82 | :param kwargs: optional fields 83 | :return: netbox object if successful otherwise exception raised 84 | """ 85 | required_fields = {"name": name, "slug": slug} 86 | return self.netbox_con.post('/dcim/sites/', required_fields, **kwargs) 87 | 88 | def delete_site(self, site_name): 89 | """Delete site 90 | 91 | :param site_name: Site to delete 92 | :return: bool True if succesful otherwise delete exception 93 | """ 94 | try: 95 | site_id = self.get_sites(name=site_name)[0]['id'] 96 | except IndexError: 97 | raise exceptions.NotFoundException({"detail": "site: {}".format(site_name)}) from None 98 | return self.netbox_con.delete('/dcim/sites/', site_id) 99 | 100 | def delete_site_by_id(self, site_id): 101 | """Delete site 102 | 103 | :param site_id: Site to delete 104 | :return: bool True if succesful otherwise raise exception 105 | """ 106 | return self.netbox_con.delete('/dcim/sites/', site_id) 107 | 108 | def update_site(self, site_name, **kwargs): 109 | """ 110 | 111 | :param site_name: Site to update 112 | :param kwargs: requests body dict 113 | :return: bool True if successful otherwise raise UpdateException 114 | """ 115 | try: 116 | site_id = self.get_sites(name=site_name)[0]['id'] 117 | except IndexError: 118 | raise exceptions.NotFoundException({"detail": "site: {}".format(site_name)}) from None 119 | return self.netbox_con.patch('/dcim/sites/', site_id, **kwargs) 120 | 121 | def update_site_by_id(self, site_id, **kwargs): 122 | """Update a site by id 123 | 124 | :param site_id: Site to update 125 | :param kwargs: requests body dict 126 | :return: bool True if successful otherwise raise Exception 127 | """ 128 | return self.netbox_con.patch('/dcim/sites/', site_id, **kwargs) 129 | 130 | def get_racks(self, **kwargs): 131 | """Returns all available racks""" 132 | return self.netbox_con.get('/dcim/racks/', **kwargs) 133 | 134 | def create_rack(self, name, site_name, **kwargs): 135 | """Create new rack 136 | 137 | :param name: Organizational rack name 138 | :param site_name: The site at which the rack exists 139 | :param kwargs: Optional arguments 140 | :return: netbox object if successful otherwise create exception 141 | """ 142 | try: 143 | site_id = self.get_sites(name=site_name)[0]['id'] 144 | except IndexError: 145 | raise exceptions.NotFoundException({"detail": "site: {}".format(site_name)}) from None 146 | required_fields = {"name": name, "site": site_id} 147 | return self.netbox_con.post('/dcim/racks/', required_fields, **kwargs) 148 | 149 | def delete_rack(self, rack_name): 150 | """Delete rack 151 | 152 | :param rack_name: Name of the rack to delete 153 | :return: bool True if successful otherwise raise DeleteException 154 | """ 155 | try: 156 | rack_id = self.get_racks(name=rack_name)[0]['id'] 157 | except IndexError: 158 | raise exceptions.NotFoundException({"detail": "rack: {}".format(rack_name)}) from None 159 | return self.netbox_con.delete('/dcim/racks/', rack_id) 160 | 161 | def delete_rack_by_id(self, rack_id): 162 | """Delete rack 163 | 164 | :param rack_id: Rack to delete 165 | :return: bool True if successful otherwise raise Exception 166 | """ 167 | return self.netbox_con.delete('/dcim/racks/', rack_id) 168 | 169 | def update_rack(self, rack_name, **kwargs): 170 | """ 171 | 172 | :param rack_name: Rack to update 173 | :param kwargs: requests body dict 174 | :return: bool True if successful otherwise raise UpdateException 175 | """ 176 | try: 177 | rack_id = self.get_racks(facility_id=rack_name)[0]['id'] 178 | except IndexError: 179 | raise exceptions.NotFoundException({"detail": "rack: {}".format(rack_name)}) from None 180 | return self.netbox_con.patch('/dcim/racks/', rack_id, **kwargs) 181 | 182 | def update_rack_by_id(self, rack_id, **kwargs): 183 | """ 184 | 185 | :param rack_id: Rack group to update 186 | :param kwargs: requests body dict 187 | :return: bool True if successful otherwise raise Exception 188 | """ 189 | return self.netbox_con.patch('/dcim/racks/', rack_id, **kwargs) 190 | 191 | def get_rack_groups(self, **kwargs): 192 | """Returns all available rack groups""" 193 | return self.netbox_con.get('/dcim/rack-groups/', **kwargs) 194 | 195 | def create_rack_group(self, name, slug, site_name, **kwargs): 196 | """Create new rack group 197 | 198 | :param name: Rack group name 199 | :param slug: slug name 200 | :param site_name: The site at which the rack exists 201 | :param kwargs: Optional arguments 202 | :return: netbox object if successful otherwise create exception 203 | """ 204 | try: 205 | site_id = self.get_sites(name=site_name)[0]['id'] 206 | except IndexError: 207 | raise exceptions.NotFoundException({"detail": "site: {}".format(site_name)}) from None 208 | required_fields = {"name": name, "slug": slug, "site": site_id} 209 | return self.netbox_con.post('/dcim/rack-groups/', required_fields, **kwargs) 210 | 211 | def delete_rack_group(self, name): 212 | """Delete rack group 213 | 214 | :param name: Name of the rack group to delete 215 | :return: bool True if successful otherwise raise DeleteException 216 | """ 217 | try: 218 | rack_group_id = self.get_rack_groups(name=name)[0]['id'] 219 | except IndexError: 220 | raise exceptions.NotFoundException({"detail": "rack-group: {}".format(name)}) from None 221 | return self.netbox_con.delete('/dcim/rack-groups/', rack_group_id) 222 | 223 | def delete_rack_group_by_id(self, rack_group_id): 224 | """Delete rack group 225 | 226 | :param rack_group_id: Rack group to delete 227 | :return: bool True if successful otherwise raise Exception 228 | """ 229 | return self.netbox_con.delete('/dcim/rack-groups/', rack_group_id) 230 | 231 | def update_rack_group(self, name, **kwargs): 232 | """ 233 | 234 | :param name: Rack group to update 235 | :param kwargs: requests body dict 236 | :return: bool True if successful otherwise raise UpdateException 237 | """ 238 | try: 239 | rack_group_id = self.get_rack_groups(name=name)[0]['id'] 240 | except IndexError: 241 | raise exceptions.NotFoundException({"detail": "rack group: {}".format(name)}) from None 242 | return self.netbox_con.patch('/dcim/rack-groups/', rack_group_id, **kwargs) 243 | 244 | def update_rack_group_by_id(self, rack_group_id, **kwargs): 245 | """ 246 | 247 | :param rack_group_id: Rack group to update 248 | :param kwargs: requests body dict 249 | :return: bool True if successful otherwise raise Exception 250 | """ 251 | return self.netbox_con.patch('/dcim/rack-groups/', rack_group_id, **kwargs) 252 | 253 | def get_devices(self, **kwargs): 254 | """Get all devices""" 255 | return self.netbox_con.get('/dcim/devices/', **kwargs) 256 | 257 | def get_devices_per_rack(self, rack_name, **kwargs): 258 | """Get devices which belongs to the given rack 259 | 260 | :param rack_name: Name of the rack 261 | :return: list of devices otherwise an empty list 262 | """ 263 | try: 264 | rack_id = self.get_racks(name=rack_name)[0]['id'] 265 | except IndexError: 266 | raise exceptions.NotFoundException({"detail": "rack: {}".format(rack_name)}) from None 267 | return self.netbox_con.get('/dcim/devices', rack_id=rack_id, **kwargs) 268 | 269 | def create_device(self, name, device_role, site_name, device_type, **kwargs): 270 | """Create a new device 271 | 272 | :param name: Name of the device 273 | :param device_role: Device role for the device 274 | :param site_name: Name of the site where the device is created 275 | :param device_type: Type for the new device 276 | :param kwargs: Optional arguments 277 | :return: netbox object if successful otherwise raise CreateException 278 | """ 279 | required_fields = {"name": name} 280 | try: 281 | device_role_id = self.get_device_roles(name=device_role)[0]['id'] 282 | required_fields.update({"device_role": device_role_id}) 283 | except IndexError: 284 | raise exceptions.NotFoundException({"detail": "device-role {}".format(device_role)}) from None 285 | 286 | try: 287 | site_id = self.get_sites(name=site_name)[0]['id'] 288 | required_fields.update({"site": site_id}) 289 | except IndexError: 290 | raise exceptions.NotFoundException({"detail": "site: {}".format(site_name)}) from None 291 | 292 | try: 293 | device_type_id = self.get_device_types(model=device_type)[0]['id'] 294 | required_fields.update({"device_type": device_type_id}) 295 | except IndexError: 296 | raise exceptions.NotFoundException({"detail": "device-type: {}".format(device_type)}) from None 297 | 298 | return self.netbox_con.post('/dcim/devices/', required_fields, **kwargs) 299 | 300 | def delete_device(self, device_name): 301 | """Delete device by device name 302 | 303 | :param device_name: Device to delete 304 | :return: bool True if successful otherwise raise DeleteException 305 | """ 306 | try: 307 | device_id = self.get_devices(name=device_name)[0]['id'] 308 | except IndexError: 309 | raise exceptions.NotFoundException({"detail": "device: {}".format(device_name)}) from None 310 | return self.netbox_con.delete('/dcim/devices/', device_id) 311 | 312 | def delete_device_by_id(self, device_id): 313 | """Delete device 314 | 315 | :param device_id: Device to delete 316 | :return: bool True if successful otherwise raise Exception 317 | """ 318 | return self.netbox_con.delete('/dcim/devices/', device_id) 319 | 320 | def update_device(self, device_name, **kwargs): 321 | """Update device by device name 322 | 323 | :param device_name: device name to update 324 | :param kwargs: requests body dict 325 | :return: bool True if successful otherwise raise UpdateException 326 | """ 327 | device_id = self.get_devices(name=device_name)[0]['id'] 328 | return self.netbox_con.patch('/dcim/devices/', device_id, **kwargs) 329 | 330 | def update_device_by_id(self, device_id, **kwargs): 331 | """Update device by id 332 | 333 | :param device_id: Device to update 334 | :param kwargs: requests body dict 335 | :return: bool True if successful otherwise raise UpdateException 336 | """ 337 | return self.netbox_con.patch('/dcim/devices/', device_id, **kwargs) 338 | 339 | def get_device_types(self, **kwargs): 340 | """Get devices by device type""" 341 | return self.netbox_con.get('/dcim/device-types/', **kwargs) 342 | 343 | def create_device_type(self, model, slug, manufacturer, **kwargs): 344 | """Create device type 345 | 346 | :param model: Model name 347 | :param slug: Slug name 348 | :param manufacturer: Name of the manufacurer 349 | :param kwargs: optional arguments 350 | :return: netbox object if successful otherwise raise CreateException 351 | """ 352 | required_fields = {"model": model, "slug": slug, "manufacturer": manufacturer} 353 | return self.netbox_con.post('/dcim/device-types/', required_fields, **kwargs) 354 | 355 | def update_device_type(self, device_type, **kwargs): 356 | """Update device type 357 | 358 | :param device_type: device-type to update 359 | :param kwargs: requests body dict 360 | :return: bool True if successful otherwise raise UpdateException 361 | """ 362 | try: 363 | device_type_id = self.get_device_types(model=device_type)[0]['id'] 364 | except IndexError: 365 | raise exceptions.NotFoundException({"detail": "device-type: {}".format(device_type)}) from None 366 | return self.netbox_con.patch('/dcim/device-types/', device_type_id, **kwargs) 367 | 368 | def update_device_type_by_id(self, device_type_id, **kwargs): 369 | """Update device type 370 | 371 | :param device_type_id: device-type to update 372 | :param kwargs: requests body dict 373 | :return: bool True if successful otherwise raise UpdateException 374 | """ 375 | return self.netbox_con.patch('/dcim/device-types/', device_type_id, **kwargs) 376 | 377 | def delete_device_type(self, model_name): 378 | """Delete device type 379 | 380 | :param model_name: Name of the model 381 | :return: bool True if successful otherwise raise DeleteException 382 | """ 383 | try: 384 | device_type_id = self.get_device_types(model=model_name)[0]['id'] 385 | except IndexError: 386 | raise exceptions.NotFoundException({"detail": "device-type: {}".format(model_name)}) from None 387 | return self.netbox_con.delete('/dcim/device-types/', device_type_id) 388 | 389 | def delete_device_type_by_id(self, device_type_id): 390 | """Delete device type 391 | 392 | :param device_type_id: Id of the device-type to delete 393 | :return: bool True if successful otherwise raise Exception 394 | """ 395 | return self.netbox_con.delete('/dcim/device-types/', device_type_id) 396 | 397 | def get_device_roles(self, **kwargs): 398 | """Return all the device roles""" 399 | return self.netbox_con.get('/dcim/device-roles/', **kwargs) 400 | 401 | def create_device_role(self, name, color, slug, **kwargs): 402 | """Create device role 403 | 404 | :param name: Role name 405 | :param color: HTML color code 406 | :param slug: Slug name 407 | :param kwargs: optional arguments 408 | :return: netbox object if successful otherwise CreateException 409 | """ 410 | required_fields = {"name": name, "color": color, "slug": slug} 411 | return self.netbox_con.post('/dcim/device-roles/', required_fields, **kwargs) 412 | 413 | def update_device_role(self, device_role, **kwargs): 414 | """Update device role 415 | 416 | :param device_role: device-role to update 417 | :param kwargs: requests body dict 418 | :return: bool True if successful otherwise raise UpdateException 419 | """ 420 | try: 421 | device_role_id = self.get_device_roles(name=device_role)[0]['id'] 422 | except IndexError: 423 | raise exceptions.NotFoundException({"detail": "device-role: {}".format(device_role)}) from None 424 | return self.netbox_con.patch('/dcim/device-roles/', device_role_id, **kwargs) 425 | 426 | def update_device_role_by_id(self, device_role_id, **kwargs): 427 | """Update device role 428 | 429 | :param device_role_id: device role to update 430 | :param kwargs: requests body dict 431 | :return: bool True if successful otherwise raise Exception 432 | """ 433 | return self.netbox_con.patch('/dcim/device-roles/', device_role_id, **kwargs) 434 | 435 | def delete_device_role(self, device_role): 436 | """Delete device by device role 437 | 438 | :param device_role: name of the role 439 | :return: bool True if successful otherwise raise DeleteException 440 | """ 441 | try: 442 | device_role_id = self.get_device_roles(name=device_role)[0]['id'] 443 | except IndexError: 444 | raise exceptions.NotFoundException({"detail": "device-role: {}".format(device_role)}) from None 445 | return self.netbox_con.delete('/dcim/device-roles/', device_role_id) 446 | 447 | def delete_device_role_by_id(self, device_role_id): 448 | """Delete device role 449 | 450 | :param device_role_id: device role to delete 451 | :return: bool True if successful otherwise raise DeleteException 452 | """ 453 | return self.netbox_con.delete('/dcim/device-roles/', device_role_id) 454 | 455 | def get_manufacturers(self, **kwargs): 456 | """Return all manufactures""" 457 | return self.netbox_con.get('/dcim/manufacturers/', **kwargs) 458 | 459 | def create_manufacturer(self, name, slug, **kwargs): 460 | """Create new manufacturer 461 | 462 | :param name: Name of manufacturer 463 | :param slug: Name of slug 464 | :param kwargs: Optional arguments 465 | :return: netbox object if successful otherwise raise CreateException 466 | """ 467 | required_fields = {"name": name, "slug": slug} 468 | return self.netbox_con.post('/dcim/manufacturers/', required_fields, **kwargs) 469 | 470 | def update_manufacturer(self, manufacturer_name, **kwargs): 471 | """Update manufacturer 472 | 473 | :param manufacturer_name: manufacturer name to update 474 | :param kwargs: requests body dict 475 | :return: bool True if successful otherwise raise UpdateException 476 | """ 477 | try: 478 | manufacturer_id = self.get_manufacturers(name=manufacturer_name)[0]['id'] 479 | except IndexError: 480 | raise exceptions.NotFoundException({"detail": "manufacturer: {}".format(manufacturer_name)}) from None 481 | return self.netbox_con.patch('/dcim/manufacturer/', manufacturer_id, **kwargs) 482 | 483 | def update_manufacturer_by_id(self, manufacturer_id, **kwargs): 484 | """Update manufacturer 485 | 486 | :param manufacturer_id: manufacturer to update 487 | :param kwargs: requests body dict 488 | :return: bool True if successful otherwise raise Exception 489 | """ 490 | return self.netbox_con.patch('/dcim/manufacturer/', manufacturer_id, **kwargs) 491 | 492 | def delete_manufacturer(self, manufacturer_name): 493 | """Delete manufacturer 494 | 495 | :param manufacturer_name: Name of manufacturer to delete 496 | :return: bool True if successful otherwise raise DeleteException 497 | """ 498 | try: 499 | manufacturer_id = self.get_manufacturers(name=manufacturer_name)[0]['id'] 500 | except IndexError: 501 | raise exceptions.NotFoundException({"detail": "manufacturer: {}".format(manufacturer_name)}) from None 502 | return self.netbox_con.delete('/dcim/manufacturers/', manufacturer_id) 503 | 504 | def delete_manufacturer_id(self, manufacturer_id): 505 | """Delete manufacturer 506 | 507 | :param manufacturer_id: manufacturer to delete 508 | :return: bool True if successful otherwise raise Exception 509 | """ 510 | return self.netbox_con.delete('/dcim/manufacturers/', manufacturer_id) 511 | 512 | def get_platforms(self, **kwargs): 513 | """Return all platforms""" 514 | return self.netbox_con.get('/dcim/platforms', **kwargs) 515 | 516 | def create_platform(self, name, slug, **kwargs): 517 | """Create new platform 518 | 519 | :param name: Name of platform 520 | :param slug: Name of slug 521 | :param kwargs: Optional arguments 522 | :return: 523 | """ 524 | required_fields = {"name": name, "slug": slug} 525 | return self.netbox_con.post('/dcim/platforms/', required_fields, **kwargs) 526 | 527 | def update_platform(self, platform_name, **kwargs): 528 | """Update platform 529 | 530 | :param platform_name: device name to update 531 | :param kwargs: requests body dict 532 | :return: bool True if successful otherwise raise UpdateException 533 | """ 534 | try: 535 | platform_id = self.get_platforms(name=platform_name)[0]['id'] 536 | except IndexError: 537 | raise exceptions.NotFoundException({"detail": "platform: {}".format(platform_name)}) from None 538 | return self.netbox_con.patch('/dcim/platforms/', platform_id, **kwargs) 539 | 540 | def update_platform_by_id(self, platform_id, **kwargs): 541 | """Update platform 542 | 543 | :param platform_id: platform to update 544 | :param kwargs: requests body dict 545 | :return: bool True if successful otherwise raise Exception 546 | """ 547 | return self.netbox_con.patch('/dcim/platforms/', platform_id, **kwargs) 548 | 549 | def delete_platform(self, platform_name): 550 | """Delete platform 551 | 552 | :param platform_name: Name of platform to delete 553 | :return: bool True if successful otherwise raise DeleteException 554 | """ 555 | try: 556 | platform_id = self.get_platforms(name=platform_name)[0]['id'] 557 | except IndexError: 558 | raise exceptions.NotFoundException({"detail": "platform: {}".format(platform_name)}) from None 559 | return self.netbox_con.delete('/dcim/platforms/', platform_id) 560 | 561 | def delete_platform_by_id(self, platform_id): 562 | """Delete platform 563 | 564 | :param platform_id: platform to delete 565 | :return: bool True if successful otherwise raise Exception 566 | """ 567 | return self.netbox_con.delete('/dcim/platforms/', platform_id) 568 | 569 | def get_interfaces(self, **kwargs): 570 | """Return interfaces""" 571 | return self.netbox_con.get('/dcim/interfaces', **kwargs) 572 | 573 | def create_interface(self, name, interface_type, device_id, **kwargs): 574 | """Create a new interface 575 | 576 | :param name: name of the interface 577 | :param interface_type: interface type. It is not possible to get the list of types from the api. Search in the netbox code for the correct type number. 578 | :param kwargs: optional arguments 579 | :param device_id: ID of the device to associate interface with 580 | :return: netbox object if successful otherwise raise CreateException 581 | """ 582 | required_fields = {"name": name, "type": interface_type, "device": device_id} 583 | return self.netbox_con.post('/dcim/interfaces/', required_fields, **kwargs) 584 | 585 | def update_interface(self, interface, device, **kwargs): 586 | """Update interface 587 | 588 | :param interface: interface to update 589 | :param device: name of the device 590 | :param kwargs: requests body dict 591 | :return: bool True if successful otherwise raise UpdateException 592 | """ 593 | try: 594 | interface_id = self.get_interfaces(name=interface, device=device)[0]['id'] 595 | except IndexError: 596 | raise exceptions.NotFoundException({"detail": "interface: {}".format(interface)}) from None 597 | return self.netbox_con.patch('/dcim/interfaces/', interface_id, **kwargs) 598 | 599 | def update_interface_by_id(self, interface_id, **kwargs): 600 | """Update interface 601 | 602 | :param interface_id: interface to update 603 | :param kwargs: requests body dict 604 | :return: bool True if successful otherwise raise Exception 605 | """ 606 | return self.netbox_con.patch('/dcim/interfaces/', interface_id, **kwargs) 607 | 608 | def delete_interface(self, interface_name, device): 609 | """Delete interface 610 | 611 | :param interface_name: Name of interface to delete 612 | :param device: Device to which the interface belongs 613 | :return: bool True if successful otherwise raise DeleteException 614 | """ 615 | try: 616 | interface_id = self.get_interfaces(name=interface_name, device=device)[0]['id'] 617 | except IndexError: 618 | raise exceptions.NotFoundException({"detail": "interface: {}".format(interface_name)}) from None 619 | return self.netbox_con.delete('/dcim/interfaces/', interface_id) 620 | 621 | def delete_interface_by_id(self, interface_id): 622 | """Delete interface 623 | 624 | :param interface_id: interface to delete 625 | :return: bool True if successful otherwise raise DeleteException 626 | """ 627 | return self.netbox_con.delete('/dcim/interfaces/', interface_id) 628 | 629 | def get_interface_connections(self, **kwargs): 630 | """Get interface connections 631 | 632 | :param kwargs: Filter arguments 633 | :return: list of interface connections 634 | """ 635 | return self.netbox_con.get('/dcim/interface-connections/', **kwargs) 636 | 637 | def get_interface_templates(self, **kwargs): 638 | """Return interface templates""" 639 | return self.netbox_con.get('/dcim/interface-templates', **kwargs) 640 | 641 | def create_interface_template(self, name, device_type, **kwargs): 642 | """Create a new interface template 643 | 644 | :param name: rack_grounamep_id of the interface 645 | :param kwargs: optional arguments 646 | :param device_type: name of the device_type to associate template with 647 | :return: netbox object if successful otherwise raise CreateException 648 | """ 649 | try: 650 | device_type_id = self.get_device_types(model=device_type)[0]['id'] 651 | except IndexError: 652 | raise exceptions.NotFoundException({"detail": "device-type: {}".format(device_type)}) from None 653 | required_fields = {"name": name, "device_type": device_type_id} 654 | return self.netbox_con.post('/dcim/interface-templates/', required_fields, **kwargs) 655 | 656 | def update_interface_template(self, interface_template_name, **kwargs): 657 | """Update interface template 658 | 659 | :param interface_template_name: interface template to update 660 | :param kwargs: requests body dict 661 | :return: bool True if successful otherwise raise UpdateException 662 | """ 663 | try: 664 | interface_template_id = self.get_interface_templates(name=interface_template_name)[0]['id'] 665 | except IndexError: 666 | raise exceptions.NotFoundException({"detail": "interface: {}".format(interface_template_name)}) from None 667 | return self.netbox_con.patch('/dcim/interface-templates/', interface_template_id, **kwargs) 668 | 669 | def update_interface_template_by_id(self, interface_template_id, **kwargs): 670 | """Update interface template 671 | 672 | :param interface_template_id: interface template to update 673 | :param kwargs: requests body dict 674 | :return: bool True if successful otherwise raise Exception 675 | """ 676 | return self.netbox_con.patch('/dcim/interface-templates/', interface_template_id, **kwargs) 677 | 678 | def delete_interface_template(self, interface_template_name): 679 | """Delete interface template 680 | 681 | :param interface_template_name: Name of interface template to delete 682 | :return: bool True if successful otherwise raise DeleteException 683 | """ 684 | try: 685 | interface_template_id = self.get_interface_templates(name=interface_template_name)[0]['id'] 686 | except IndexError: 687 | raise exceptions.NotFoundException({"detail": "interface-template: {}".format(interface_template_name)}) from None 688 | return self.netbox_con.delete('/dcim/interface-templates/', interface_template_id) 689 | 690 | def delete_interface_template_by_id(self, interface_template_id): 691 | """Delete interface template 692 | 693 | :param interface_template_id: Name of interface template to delete 694 | :return: bool True if successful otherwise raise DeleteException 695 | """ 696 | return self.netbox_con.delete('/dcim/interface-templates/', interface_template_id) 697 | 698 | def get_inventory_items(self, **kwargs): 699 | """Return inventory items""" 700 | return self.netbox_con.get('/dcim/inventory-items/', **kwargs) 701 | 702 | def create_inventory_item(self, name, device_name, **kwargs): 703 | """Create inventory item 704 | 705 | :param name: Inventory item name 706 | :param device_name: Name of device 707 | :param kwargs: Extra inventory parameters 708 | :return: netbox object if successful otherwise raise CreateException 709 | """ 710 | try: 711 | device_id = self.get_devices(name=device_name)[0]['id'] 712 | except IndexError: 713 | raise exceptions.NotFoundException({"detail": "device: {}".format(device_name)}) from None 714 | required_fields = {"name": name, "device": device_id} 715 | return self.netbox_con.post('/dcim/inventory-items/', required_fields, **kwargs) 716 | 717 | def update_inventory_item(self, name, device_name, **kwargs): 718 | """Update inventory item 719 | 720 | :param name: Inventory item name 721 | :param device_name: Name of device 722 | :param kwargs: Extra inventory items to update 723 | :return bool True if successful otherwise raise UpdateException 724 | """ 725 | try: 726 | inventory_item_id = self.get_inventory_items(name=name, device=device_name)[0]['id'] 727 | except IndexError: 728 | raise exceptions.NotFoundException({"detail": "inventory item: {}".format(name)}) from None 729 | return self.netbox_con.patch('/dcim/inventory-items/', inventory_item_id, **kwargs) 730 | 731 | def update_inventory_item_by_id(self, inventory_item_id, **kwargs): 732 | """Update inventory item 733 | 734 | :param inventory_item_id: Inventory item to delete 735 | :param kwargs: Extra inventory items to update 736 | :return bool True if successful otherwise raise Exception 737 | """ 738 | return self.netbox_con.patch('/dcim/inventory-items/', inventory_item_id, **kwargs) 739 | 740 | def delete_inventory_item(self, name, device_name): 741 | """Delete inventory item 742 | 743 | :param name: Name of inventory item to delete 744 | :param device_name: Name of the device to remove inventory item from 745 | :return: bool True if successful otherwise raise DeleteException 746 | """ 747 | try: 748 | inventory_item_id = self.get_inventory_items(name=name, device=device_name)[0]['id'] 749 | except IndexError: 750 | raise exceptions.NotFoundException({"detail": "inventory item: {}".format(name)}) from None 751 | return self.netbox_con.delete('/dcim/inventory-items/', inventory_item_id) 752 | 753 | def delete_inventory_item_by_id(self, inventory_item_id): 754 | """Delete inventory item 755 | 756 | :param inventory_item_id: Name of inventory item to delete 757 | :return: bool True if successful otherwise raise DeleteException 758 | """ 759 | return self.netbox_con.delete('/dcim/inventory-items/', inventory_item_id) 760 | 761 | def get_power_outlets(self, **kwargs): 762 | """Get power outlets matching optional kwarg filters""" 763 | return self.netbox_con.get('/dcim/power-outlets/', **kwargs) 764 | 765 | def get_power_ports(self, **kwargs): 766 | """Return power ports""" 767 | return self.netbox_con.get('/dcim/power-ports/', **kwargs) 768 | 769 | def get_power_connections(self, **kwargs): 770 | """Return power connections """ 771 | return self.netbox_con.get('/dcim/power-connections/', **kwargs) 772 | 773 | def create_location(self, name, slug, site_name, **kwargs): 774 | """Create a location 775 | 776 | :param name: Location name 777 | :param slug: Slug for the location 778 | :param site_name: Name of the site to connect the location to 779 | :param kwargs: Extra location parameters 780 | :return: netbox object if successful otherwise raise CreateException 781 | """ 782 | try: 783 | site_id = self.get_sites(name=site_name)[0]['id'] 784 | except IndexError: 785 | raise exceptions.NotFoundException({"detail": "site: {}".format(site_name)}) from None 786 | required_fields = {"name": name, "slug": slug, "site": site_id} 787 | return self.netbox_con.post('/dcim/locations/', required_fields, **kwargs) 788 | 789 | def get_locations(self, **kwargs): 790 | """Return locations""" 791 | return self.netbox_con.get('/dcim/locations/', **kwargs) 792 | 793 | def update_location(self, location_id, **kwargs): 794 | """Update location 795 | 796 | :param location: location item to update 797 | :param kwargs: Extra location items to update 798 | :return bool True if successful otherwise raise Exception 799 | """ 800 | return self.netbox_con.patch('/dcim/locations/', location_id, **kwargs) 801 | 802 | def delete_location(self, location_name): 803 | """Delete location 804 | 805 | :param location_name: Name of location to delete 806 | :return: bool True if successful otherwise raise DeleteException 807 | """ 808 | try: 809 | location_id = self.get_locations(name=location_name)[0]['id'] 810 | except IndexError: 811 | raise exceptions.NotFoundException({"detail": "location: {}".format(location_name)}) from None 812 | return self.netbox_con.delete('/dcim/locations/', location_id) -------------------------------------------------------------------------------- /netbox/exceptions.py: -------------------------------------------------------------------------------- 1 | class GeneralException(BaseException): 2 | def __init__(self, resp_data): 3 | if isinstance(resp_data, dict): 4 | if 'detail' in resp_data: 5 | self.err = resp_data['detail'] 6 | else: 7 | self.err = ''.join('{} '.format(val[0]) for key, val in resp_data.items()) 8 | else: 9 | self.err = 'Unknown Error, please check the Netbox logs' 10 | 11 | def __str__(self): 12 | return '{}'.format(self.err) 13 | 14 | 15 | class DeleteException(GeneralException): 16 | """Raised when delete failed""" 17 | def __init__(self, resp_data): 18 | super().__init__(resp_data) 19 | 20 | 21 | class NotFoundException(GeneralException): 22 | """Raised when item is not found""" 23 | def __init__(self, resp_data): 24 | super().__init__(resp_data) 25 | 26 | 27 | class GetException(GeneralException): 28 | pass 29 | 30 | 31 | class CreateException(GeneralException): 32 | """Raised when creation failed""" 33 | def __init__(self, resp_data): 34 | super().__init__(resp_data) 35 | 36 | 37 | class UpdateException(BaseException): 38 | """Raised when an object update fails""" 39 | def __init__(self, resp_data): 40 | super().__init__(resp_data) 41 | 42 | 43 | class AuthException(BaseException): 44 | """Raised when an API call method is not allowed""" 45 | pass 46 | 47 | 48 | class AuthorizationException(GeneralException): 49 | """HTTP 403 status code""" 50 | def __init__(self, resp_data): 51 | super().__init__(resp_data) 52 | 53 | 54 | class ClientException(GeneralException): 55 | """HTTP 400 status code""" 56 | def __init__(self, resp_data): 57 | super().__init__(resp_data) 58 | 59 | 60 | class ServerException(GeneralException): 61 | """HTTP 5xx status code""" 62 | def __init__(self, resp_data): 63 | super().__init__(resp_data) 64 | -------------------------------------------------------------------------------- /netbox/extras.py: -------------------------------------------------------------------------------- 1 | import netbox.exceptions as exceptions 2 | 3 | 4 | class Extras(object): 5 | 6 | def __init__(self, netbox_con): 7 | self.netbox_con = netbox_con 8 | 9 | def get_config_contexts(self, **kwargs): 10 | """Returns all config-contexts""" 11 | return self.netbox_con.get('/extras/config-contexts/', **kwargs) 12 | 13 | def create_config_context(self, name, data, **kwargs): 14 | """Create a config-context 15 | 16 | :param name: config-context name 17 | :param data: data in json format 18 | :param kwargs: optional fields 19 | :return: netbox object if successful otherwise exception raised 20 | """ 21 | required_fields = {"name": name, "data": data} 22 | return self.netbox_con.post('/extras/config-contexts/', required_fields, **kwargs) 23 | 24 | def delete_config_context(self, name): 25 | """Delete config-context 26 | 27 | :param name: Name of the config-context to delete 28 | :return: bool True if succesful otherwise delete exception 29 | """ 30 | try: 31 | config_context_id = self.get_config_contexts(name=name)[0]['id'] 32 | except IndexError: 33 | raise exceptions.NotFoundException({"detail": "config-context: {}".format(name)}) from None 34 | return self.netbox_con.delete('/extras/config-contexts/', config_context_id) 35 | 36 | def delete_config_context_by_id(self, config_context_id): 37 | """Delete config-context 38 | 39 | :param config_context_id: config-context to delete 40 | :return: bool True if succesful otherwise delete exception 41 | """ 42 | return self.netbox_con.delete('/extras/config-contexts/', config_context_id) 43 | 44 | def update_config_context(self, name, **kwargs): 45 | """Update config-context 46 | 47 | :param name: config-context to update 48 | :param kwargs: requests body dict 49 | :return: bool True if successful otherwise raise UpdateException 50 | """ 51 | try: 52 | config_context_id = self.get_config_contexts(name=name)[0]['id'] 53 | except IndexError: 54 | raise exceptions.NotFoundException({"detail": "config-context: {}".format(name)}) from None 55 | 56 | return self.netbox_con.patch('/extras/config-contexts/', config_context_id, **kwargs) 57 | 58 | def update_config_context_by_id(self, config_context_id, **kwargs): 59 | """Update config-context 60 | 61 | :param config_context_id: config-context to update 62 | :param kwargs: requests body dict 63 | :return: bool True if successful otherwise raise UpdateException 64 | """ 65 | return self.netbox_con.patch('/extras/config-contexts/', config_context_id, **kwargs) 66 | 67 | def get_tags(self, **kwargs): 68 | """Returns all tags""" 69 | return self.netbox_con.get('/extras/tags/', **kwargs) 70 | 71 | def create_tag(self, name, slug, **kwargs): 72 | """Create a tag 73 | 74 | :param name: tag name 75 | :param slug: tag slug 76 | :param kwargs: optional fields 77 | :return: netbox object if successful otherwise exception raised 78 | """ 79 | required_fields = {"name": name, "slug": slug} 80 | return self.netbox_con.post('/extras/tags/', required_fields, **kwargs) 81 | 82 | def delete_tag(self, name): 83 | """Delete tag 84 | 85 | :param name: Name of the tag to delete 86 | :return: bool True if succesful otherwise delete exception 87 | """ 88 | try: 89 | tag_id = self.get_tags(name=name)[0]['id'] 90 | except IndexError: 91 | raise exceptions.NotFoundException({"detail": "tag: {}".format(name)}) from None 92 | return self.netbox_con.delete('/extras/tags/', tag_id) 93 | 94 | def delete_tag_by_id(self, tag_id): 95 | """Delete tag 96 | 97 | :param tag_id: tag to delete 98 | :return: bool True if succesful otherwise delete exception 99 | """ 100 | return self.netbox_con.delete('/extras/tags/', tag_id) 101 | 102 | def update_tag(self, name, **kwargs): 103 | """Update tag 104 | 105 | :param name: tag name 106 | :param kwargs: requests body dict 107 | :return: bool True if successful otherwise raise UpdateException 108 | """ 109 | try: 110 | tag_id = self.get_tags(name=name)[0]['id'] 111 | except IndexError: 112 | raise exceptions.NotFoundException({"detail": "tag: {}".format(name)}) from None 113 | 114 | return self.netbox_con.patch('/extras/tags/', tag_id, **kwargs) 115 | 116 | def update_tag_by_id(self, tag_id, **kwargs): 117 | """Update tag 118 | 119 | :param tag_id: tag id 120 | :param kwargs: requests body dict 121 | :return: bool True if successful otherwise raise UpdateException 122 | """ 123 | return self.netbox_con.patch('/extras/tags/', tag_id, **kwargs) 124 | 125 | def get_object_changes(self, **kwargs): 126 | """Returns all object changes""" 127 | return self.netbox_con.get('/extras/object-changes/', **kwargs) 128 | 129 | def get_reports(self): 130 | """Returns all reports""" 131 | return self.netbox_con.get('/extras/reports/') 132 | -------------------------------------------------------------------------------- /netbox/ipam.py: -------------------------------------------------------------------------------- 1 | import ipaddress 2 | from netbox import exceptions 3 | 4 | 5 | class Ipam(object): 6 | 7 | def __init__(self, netbox_con): 8 | 9 | self.netbox_con = netbox_con 10 | 11 | def get_choices(self, choice_id=None): 12 | """Return choices for all fields if choice_id is not defined 13 | 14 | :param choice_id: Optional model:field tuple 15 | """ 16 | return self.netbox_con.get('/ipam/_choices/', choice_id) 17 | 18 | def get_services(self, **kwargs): 19 | """Return all services""" 20 | return self.netbox_con.get('/ipam/services/', **kwargs) 21 | 22 | def create_service(self, name, port, protocol, **kwargs): 23 | """Create a new service definition 24 | 25 | :param name: Name of the provided service 26 | :param port: Service listening port 27 | :param protocol: Service used protocol 6 is used to TCP and 17 for UDP 28 | :param kwargs: Optional arguments (need to attach service to a 'virtual_machine: id' or 'device: id' ) 29 | :return: netbox object if successful otherwise raise CreateException 30 | """ 31 | required_fields = {"name": name, "port": port, "protocol": protocol} 32 | return self.netbox_con.post('/ipam/services/', required_fields, **kwargs) 33 | 34 | def get_ip_addresses(self, **kwargs): 35 | """Return all ip addresses""" 36 | return self.netbox_con.get('/ipam/ip-addresses/', **kwargs) 37 | 38 | def get_ip_by_device(self, device_name): 39 | """Get IPs which are associated to a device 40 | 41 | :param device_name: Name of the device 42 | :return: ip address information 43 | """ 44 | return self.netbox_con.get('/ipam/ip-addresses', device=device_name) 45 | 46 | def get_ip_by_virtual_machine(self, vm_name): 47 | """Get IPs which are associated to a virtual-machine 48 | 49 | :param vm_name: Name of the virtual machine 50 | :return: ip address information 51 | """ 52 | return self.netbox_con.get('/ipam/ip-addresses', virtual_machine=vm_name) 53 | 54 | def create_ip_address(self, address, **kwargs): 55 | """Create a new ip address 56 | 57 | :param address: IP address 58 | :param kwargs: Optional arguments 59 | :return: netbox object if successful otherwise raise CreateException 60 | """ 61 | required_fields = {"address": address} 62 | return self.netbox_con.post('/ipam/ip-addresses/', required_fields, **kwargs) 63 | 64 | def update_ip(self, ip_address, **kwargs): 65 | """Update ip address 66 | 67 | :param ip_address: ip address with prefix. Format: 1.1.1.1/32 68 | :param kwargs: requests body dict 69 | :return: bool True if successful otherwise raise UpdateException 70 | """ 71 | try: 72 | ip_id = self.get_ip_addresses(address=ip_address)[0]['id'] 73 | except IndexError: 74 | raise exceptions.NotFoundException({"detail": "ip: {}".format(ip_address)}) from None 75 | return self.netbox_con.patch('/ipam/ip-addresses/', ip_id, **kwargs) 76 | 77 | def update_ip_by_id(self, ip_id, **kwargs): 78 | """Update ip address 79 | 80 | :param ip_id: ip id to update 81 | :param kwargs: requests body dict 82 | :return: bool True if successful otherwise raise UpdateException 83 | """ 84 | return self.netbox_con.patch('/ipam/ip-addresses/', ip_id, **kwargs) 85 | 86 | def delete_ip_address(self, ip_address): 87 | """Delete IP address 88 | 89 | :param ip_address: IP address to delete 90 | :return: bool True if successful otherwise raise DeleteException 91 | """ 92 | try: 93 | ip_id = self.get_ip_addresses(address=ip_address)[0]['id'] 94 | except IndexError: 95 | raise exceptions.NotFoundException({"detail": "ip: {}".format(ip_address)}) from None 96 | return self.netbox_con.delete('/ipam/ip-addresses/', ip_id) 97 | 98 | def delete_ip_by_id(self, ip_id): 99 | """Delete IP address 100 | 101 | :param ip_id: ID of ip address to delete 102 | :return: bool True if successful otherwise raise DeleteException 103 | """ 104 | return self.netbox_con.delete('/ipam/ip-addresses/', ip_id) 105 | 106 | def get_ip_prefixes(self, **kwargs): 107 | """Return all ip prefixes""" 108 | return self.netbox_con.get('/ipam/prefixes/', **kwargs) 109 | 110 | def create_ip_prefix(self, prefix, **kwargs): 111 | """Create a new ip prefix 112 | 113 | :param prefix: A valid ip prefix format. The syntax will be checked with the ipaddress module 114 | :param kwargs: Optional arguments 115 | :return: netbox object if successful otherwise raise CreateException 116 | """ 117 | required_fields = {"prefix": prefix} 118 | 119 | if ipaddress.ip_network(prefix, strict=True): 120 | return self.netbox_con.post('/ipam/prefixes/', required_fields, **kwargs) 121 | 122 | def delete_ip_prefix(self, **kwargs): 123 | """Delete IP prefix 124 | 125 | :param kwargs: Delete prefix based on filter values 126 | :return: bool True if successful otherwise raise DeleteException 127 | """ 128 | try: 129 | ip_prefix_id = self.get_ip_prefixes(**kwargs)[0]['id'] 130 | except IndexError: 131 | raise exceptions.NotFoundException({"detail": "ip-prefix"}) from None 132 | return self.netbox_con.delete('/ipam/prefixes/', ip_prefix_id) 133 | 134 | def delete_ip_prefix_by_id(self, ip_prefix_id): 135 | """Delete IP prefix 136 | 137 | :param ip_prefix_id: Delete prefix based on id 138 | :return: bool True if successful otherwise raise DeleteException 139 | """ 140 | return self.netbox_con.delete('/ipam/prefixes/', ip_prefix_id) 141 | 142 | def update_ip_prefix(self, ip_prefix, **kwargs): 143 | """Update ip address 144 | 145 | :param ip_prefix: ip prefix to update 146 | :param kwargs: requests body dict 147 | :return: bool True if successful otherwise raise UpdateException 148 | """ 149 | try: 150 | ip_prefix_id = self.get_ip_prefixes(prefix=ip_prefix)[0]['id'] 151 | except IndexError: 152 | raise exceptions.NotFoundException({"detail": "ip-prefix: {}".format(ip_prefix)}) from None 153 | return self.netbox_con.patch('/ipam/prefixes/', ip_prefix_id, **kwargs) 154 | 155 | def update_ip_prefix_by_id(self, ip_prefix_id, **kwargs): 156 | """Update ip address 157 | 158 | :param ip_prefix_id: ip prefix to update 159 | :param kwargs: requests body dict 160 | :return: bool True if successful otherwise raise UpdateException 161 | """ 162 | return self.netbox_con.patch('/ipam/prefixes/', ip_prefix_id, **kwargs) 163 | 164 | def get_next_available_ip(self, **kwargs): 165 | """Return next available ip in prefix 166 | 167 | :param kwargs: Use the filter fields from the get_prefixes call 168 | :return: next available ip 169 | """ 170 | try: 171 | prefix_id = self.get_ip_prefixes(**kwargs)[0]['id'] 172 | except IndexError: 173 | raise exceptions.NotFoundException({"detail": "ip-prefix"}) from None 174 | 175 | param = '/ipam/prefixes/{}/available-ips/'.format(prefix_id) 176 | return self.netbox_con.get(param, limit=1)[0]['address'] 177 | 178 | def get_vrfs(self, **kwargs): 179 | """Get all vrfs""" 180 | return self.netbox_con.get('/ipam/vrfs/', **kwargs) 181 | 182 | def create_vrf(self, name, rd, **kwargs): 183 | """Create a new vrf 184 | 185 | :param name: Name of the vrf 186 | :param rd: Route distinguisher in any format 187 | :param kwargs: Optional arguments 188 | :return: netbox object if successful otherwise raise CreateException 189 | """ 190 | required_fields = {"name": name, "rd": rd} 191 | return self.netbox_con.post('/ipam/vrfs/', required_fields, **kwargs) 192 | 193 | def delete_vrf(self, vrf_name): 194 | """Delete vrf 195 | 196 | :param vrf_name: Name of vrf to delete 197 | :return: bool True if successful otherwise raise DeleteException 198 | """ 199 | try: 200 | vrf_id = self.get_vrfs(name=vrf_name)[0]['id'] 201 | except IndexError: 202 | raise exceptions.NotFoundException({"detail": "vrf: {}".format(vrf_name)}) from None 203 | return self.netbox_con.delete('/ipam/vrfs/', vrf_id) 204 | 205 | def delete_vrf_by_id(self, vrf_id): 206 | """Delete vrf 207 | 208 | :param vrf_id: vrf to delete 209 | :return: bool True if successful otherwise raise DeleteException 210 | """ 211 | return self.netbox_con.delete('/ipam/vrfs/', vrf_id) 212 | 213 | def update_vrf(self, vrf_name, **kwargs): 214 | """Update vrf 215 | 216 | :param vrf_name: name of the vrf to update 217 | :param kwargs: requests body dict 218 | :return: bool True if successful otherwise raise UpdateException 219 | """ 220 | try: 221 | vrf_id = self.get_vrfs(name=vrf_name)[0]['id'] 222 | except IndexError: 223 | raise exceptions.NotFoundException({"detail": "vrf: {}".format(vrf_name)}) from None 224 | return self.netbox_con.patch('/ipam/vrfs/', vrf_id, **kwargs) 225 | 226 | def update_vrf_by_id(self, vrf_id, **kwargs): 227 | """Update vrf 228 | 229 | :param vrf_id: vrf to update 230 | :param kwargs: requests body dict 231 | :return: bool True if successful otherwise raise UpdateException 232 | """ 233 | return self.netbox_con.patch('/ipam/vrfs/', vrf_id, **kwargs) 234 | 235 | def get_aggregates(self, **kwargs): 236 | """Return all aggregates""" 237 | return self.netbox_con.get('/ipam/aggregates/', **kwargs) 238 | 239 | def create_aggregate(self, prefix, rir, **kwargs): 240 | """Creates a new aggregate 241 | 242 | :param prefix: IP Prefix 243 | :param rir: Name of the RIR 244 | :param kwargs: Optional Arguments 245 | :return: 246 | """ 247 | rir_id = self.get_rirs(name=rir)[0]['id'] 248 | required_fields = {"prefix": prefix, "rir": rir_id} 249 | 250 | if ipaddress.ip_network(prefix, strict=True): 251 | return self.netbox_con.post('/ipam/aggregates/', required_fields, **kwargs) 252 | 253 | def update_aggregate(self, prefix, **kwargs): 254 | """Update aggregate 255 | 256 | :param prefix: Prefix of the aggregate 257 | :param kwargs: requests body dict 258 | :return: bool True if successful otherwise raise UpdateException 259 | """ 260 | try: 261 | aggregate_id = self.get_aggregates(prefix=prefix)[0]['id'] 262 | except IndexError: 263 | raise exceptions.NotFoundException({"detail": "aggregate: {}".format(prefix)}) from None 264 | return self.netbox_con.patch('/ipam/aggregates/', aggregate_id, **kwargs) 265 | 266 | def update_aggregate_by_id(self, aggregate_id, **kwargs): 267 | """Update aggregate 268 | 269 | :param aggregate_id: aggregate to update 270 | :param kwargs: requests body dict 271 | :return: bool True if successful otherwise raise UpdateException 272 | """ 273 | return self.netbox_con.patch('/ipam/aggregates/', aggregate_id, **kwargs) 274 | 275 | def get_rirs(self, **kwargs): 276 | """Return all rirs""" 277 | return self.netbox_con.get('/ipam/rirs/', **kwargs) 278 | 279 | def create_rir(self, name, slug): 280 | """Create new rir 281 | 282 | :param name: Name of the rir 283 | :param slug: Name of the slug 284 | :return: netbox object if successful otherwise raise CreateException 285 | """ 286 | required_fields = {"name": name, "slug": slug} 287 | return self.netbox_con.post('/ipam/rirs/', required_fields) 288 | 289 | def delete_rir(self, rir_name): 290 | """Delete rir 291 | 292 | :param rir_name: rir name to delete 293 | :return: bool True if successful otherwise raise DeleteException 294 | """ 295 | try: 296 | rir_id = self.get_rirs(name=rir_name)[0]['id'] 297 | except IndexError: 298 | raise exceptions.NotFoundException({"detail": "rir: {}".format(rir_name)}) from None 299 | return self.netbox_con.delete('/ipam/rirs/', rir_id) 300 | 301 | def delete_rir_by_id(self, rir_id): 302 | """Delete rir 303 | 304 | :param rir_id: rir to delete 305 | :return: bool True if successful otherwise raise DeleteException 306 | """ 307 | return self.netbox_con.delete('/ipam/rirs/', rir_id) 308 | 309 | def update_rir(self, rir_name, **kwargs): 310 | """Update rir 311 | 312 | :param rir_name: Name of the rir 313 | :param kwargs: requests body dict 314 | :return: bool True if successful otherwise raise UpdateException 315 | """ 316 | try: 317 | rir_id = self.get_rirs(name=rir_name)[0]['id'] 318 | except IndexError: 319 | raise exceptions.NotFoundException({"detail": "rir: {}".format(rir_name)}) from None 320 | return self.netbox_con.patch('/ipam/rirs/', rir_id, **kwargs) 321 | 322 | def update_rir_by_id(self, rir_id, **kwargs): 323 | """Update rir 324 | 325 | :param rir_id: rir to update 326 | :param kwargs: requests body dict 327 | :return: bool True if successful otherwise raise UpdateException 328 | """ 329 | return self.netbox_con.patch('/ipam/rirs/', rir_id, **kwargs) 330 | 331 | def get_roles(self, **kwargs): 332 | """Return all roles""" 333 | return self.netbox_con.get('/ipam/roles/', **kwargs) 334 | 335 | def create_role(self, name, slug, **kwargs): 336 | """Create new prefix/vlan role 337 | 338 | :param name: Name of the prefix/vlan role 339 | :param slug: Name of the slug 340 | :return: netbox object if successful otherwise raise CreateException 341 | """ 342 | required_fields = {"name": name, "slug": slug} 343 | return self.netbox_con.post('/ipam/roles/', required_fields, **kwargs) 344 | 345 | def delete_role(self, role_name): 346 | """Delete prefix/vlan role 347 | 348 | :param role_name: prefix/vlan role to delete 349 | :return: bool True if successful otherwise raise DeleteException 350 | """ 351 | try: 352 | role_id = self.get_roles(name=role_name)[0]['id'] 353 | except IndexError: 354 | raise exceptions.NotFoundException({"detail": "prefix/vlan role: {}".format(role_name)}) from None 355 | return self.netbox_con.delete('/ipam/roles/', role_id) 356 | 357 | def delete_role_by_id(self, role_id): 358 | """Delete prefix/vlan role 359 | 360 | :param role_id: prefix/vlan role to delete 361 | :return: bool True if successful otherwise raise DeleteException 362 | """ 363 | return self.netbox_con.delete('/ipam/roles/', role_id) 364 | 365 | def update_role(self, role_name, **kwargs): 366 | """Update prefix role 367 | 368 | :param role_name: Name of the prefix/vlan role 369 | :param kwargs: requests body dict 370 | :return: bool True if successful otherwise raise UpdateException 371 | """ 372 | try: 373 | prefix_role_id = self.get_roles(name=role_name)[0]['id'] 374 | except IndexError: 375 | raise exceptions.NotFoundException({"detail": "prefix/vlan role: {}".format(role_name)}) from None 376 | return self.netbox_con.patch('/ipam/roles/', prefix_role_id, **kwargs) 377 | 378 | def update_role_by_id(self, role_id, **kwargs): 379 | """Update prefix role 380 | 381 | :param role_id: role to update 382 | :param kwargs: requests body dict 383 | :return: bool True if successful otherwise raise UpdateException 384 | """ 385 | return self.netbox_con.patch('/ipam/roles/', role_id, **kwargs) 386 | 387 | def get_vlans(self, **kwargs): 388 | """Return all vlans""" 389 | return self.netbox_con.get('/ipam/vlans/', **kwargs) 390 | 391 | def create_vlan(self, vid, vlan_name, **kwargs): 392 | """Create new vlan 393 | 394 | :param vid: ID of the new vlan 395 | :param vlan_name: Name of the vlan 396 | :param kwargs: Optional Arguments 397 | :return: netbox object if successful otherwise raise CreateException 398 | """ 399 | required_fields = {"vid": vid, "name": vlan_name} 400 | return self.netbox_con.post('/ipam/vlans/', required_fields, **kwargs) 401 | 402 | def delete_vlan(self, vid): 403 | """Delete VLAN based on VLAN ID 404 | 405 | :param vid: vlan id to delete 406 | :return: bool True if successful otherwise raise DeleteException 407 | """ 408 | try: 409 | vid_id = self.get_vlans(vid=vid)[0]['id'] 410 | except IndexError: 411 | raise exceptions.NotFoundException({"detail": "vlan: {}".format(vid)}) from None 412 | return self.netbox_con.delete('/ipam/vlans/', vid_id) 413 | 414 | def delete_vlan_by_id(self, vlan_id): 415 | """Delete VLAN based on VLAN ID 416 | 417 | :param vlan_id: vlan id to delete 418 | :return: bool True if successful otherwise raise DeleteException 419 | """ 420 | return self.netbox_con.delete('/ipam/vlans/', vlan_id) 421 | 422 | def update_vlan(self, vlan_name, **kwargs): 423 | """Update vlan 424 | 425 | :param vlan_name: Name of the vlan 426 | :param kwargs: requests body dict 427 | :return: bool True if successful otherwise raise UpdateException 428 | """ 429 | try: 430 | vlan_id = self.get_vlans(name=vlan_name)[0]['id'] 431 | except IndexError: 432 | raise exceptions.NotFoundException({"detail": "vlan: {}".format(vlan_name)}) from None 433 | return self.netbox_con.patch('/ipam/vlans/', vlan_id, **kwargs) 434 | 435 | def update_vlan_by_id(self, vlan_id, **kwargs): 436 | """Update vlan 437 | 438 | :param vlan_id: vlan to update 439 | :param kwargs: requests body dict 440 | :return: bool True if successful otherwise raise UpdateException 441 | """ 442 | return self.netbox_con.patch('/ipam/vlans/', vlan_id, **kwargs) 443 | 444 | def get_vlan_groups(self, **kwargs): 445 | """Return all vlan groups""" 446 | return self.netbox_con.get('/ipam/vlan-groups/', **kwargs) 447 | 448 | def create_vlan_group(self, name, slug, **kwargs): 449 | """Create new vlan-group 450 | 451 | :param name: name of the vlan group 452 | :param slug: slug 453 | :param kwargs: Optional Arguments 454 | :return: netbox object if successful otherwise raise CreateException 455 | """ 456 | required_fields = {"name": name, "slug": slug} 457 | return self.netbox_con.post('/ipam/vlan-groups/', required_fields, **kwargs) 458 | 459 | def delete_vlan_group(self, name): 460 | """Delete VLAN group 461 | 462 | :param name: name of the vlan-group to delete 463 | :return: bool True if successful otherwise raise DeleteException 464 | """ 465 | try: 466 | vgrp_id = self.get_vlan_groups(name=name)[0]['id'] 467 | except IndexError: 468 | raise exceptions.NotFoundException({"detail": "vlan: {}".format(name)}) from None 469 | return self.netbox_con.delete('/ipam/vlan-groups/', vgrp_id) 470 | 471 | def delete_vlan_group_by_id(self, vlan_group_id): 472 | """Delete VLAN group 473 | 474 | :param vlan_group_id: vlan-group to delete 475 | :return: bool True if successful otherwise raise DeleteException 476 | """ 477 | return self.netbox_con.delete('/ipam/vlan-groups/', vlan_group_id) 478 | 479 | def update_vlan_group(self, name, **kwargs): 480 | """Update vlan-group 481 | 482 | :param name: name of the vlan-group 483 | :param kwargs: arguments 484 | :return: bool True if successful otherwise raise UpdateException 485 | """ 486 | try: 487 | vgrp_ip = self.get_vlan_groups(name=name)[0]['id'] 488 | except IndexError: 489 | raise exceptions.NotFoundException({"detail": "name: {}".format(name)}) from None 490 | return self.netbox_con.patch('/ipam/vlan-groups/', vgrp_ip, **kwargs) 491 | 492 | def update_vlan_group_by_id(self, vlan_group_id, **kwargs): 493 | """Update vlan-group 494 | 495 | :param vlan_group_id: vlan group to update 496 | :param kwargs: arguments 497 | :return: bool True if successful otherwise raise UpdateException 498 | """ 499 | return self.netbox_con.patch('/ipam/vlan-groups/', vlan_group_id, **kwargs) 500 | -------------------------------------------------------------------------------- /netbox/netbox.py: -------------------------------------------------------------------------------- 1 | import netbox.connection as connection 2 | import netbox.dcim as dcim 3 | import netbox.ipam as ipam 4 | import netbox.virtualization as virtualization 5 | import netbox.exceptions as exceptions 6 | import netbox.tenancy as tenancy 7 | import netbox.extras as extras 8 | import netbox.circuits as circuits 9 | import netbox.status as status 10 | 11 | 12 | class NetBox(object): 13 | def __init__(self, host, **kwargs): 14 | self.connection = connection.NetboxConnection(host=host, **kwargs) 15 | self.ipam = ipam.Ipam(self.connection) 16 | self.dcim = dcim.Dcim(self.connection) 17 | self.circuits = circuits.Circuits(self.connection) 18 | self.virtualization = virtualization.Virtualization(self.connection) 19 | self.tenancy = tenancy.Tenancy(self.connection) 20 | self.extras = extras.Extras(self.connection) 21 | self.status = status.Status(self.connection) 22 | self.exceptions = exceptions 23 | -------------------------------------------------------------------------------- /netbox/status.py: -------------------------------------------------------------------------------- 1 | class Status(object): 2 | 3 | def __init__(self, netbox_con): 4 | self.netbox_con = netbox_con 5 | 6 | def get_status(self): 7 | """Returns the Netbox status""" 8 | return self.netbox_con.get('/status/') 9 | -------------------------------------------------------------------------------- /netbox/tenancy.py: -------------------------------------------------------------------------------- 1 | import netbox.exceptions as exceptions 2 | 3 | 4 | class Tenancy(object): 5 | 6 | def __init__(self, netbox_con): 7 | self.netbox_con = netbox_con 8 | 9 | def get_choices(self, choice_id=None): 10 | """Return choices for all fields if choice_id is not defined 11 | 12 | :param choice_id: Optional model:field tuple 13 | """ 14 | return self.netbox_con.get('/tenancy/_choices/', choice_id) 15 | 16 | def get_tenants(self, **kwargs): 17 | """Returns the tenants""" 18 | return self.netbox_con.get('/tenancy/tenants/', **kwargs) 19 | 20 | def get_contacts(self, **kwargs): 21 | """Returns the contacts""" 22 | return self.netbox_con.get('/tenancy/contacts/', **kwargs) 23 | 24 | def get_contact_assignments(self, **kwargs): 25 | """Returns the contacts""" 26 | return self.netbox_con.get('/tenancy/contact-assignments/', **kwargs) 27 | 28 | def get_contact_roles(self, **kwargs): 29 | """Returns the roles for contacts""" 30 | return self.netbox_con.get('/tenancy/contact-roles/', **kwargs) 31 | 32 | def create_tenant(self, name, slug, **kwargs): 33 | """Create a new tenant 34 | 35 | :param name: Tenant name 36 | :param slug: slug name 37 | :param kwargs: optional fields 38 | :return: netbox object if successful otherwise exception raised 39 | """ 40 | required_fields = {"name": name, "slug": slug} 41 | return self.netbox_con.post('/tenancy/tenants/', required_fields, **kwargs) 42 | 43 | def create_contact(self, name: str, **kwargs): 44 | """Create a new contact 45 | 46 | :param name: Contact name 47 | :param kwargs: optional fields 48 | :return: netbox object if successful otherwise exception raised 49 | """ 50 | required_fields = {"name": name} 51 | return self.netbox_con.post('/tenancy/contacts/', required_fields, **kwargs) 52 | 53 | def create_contact_assignment_tenant(self, contact_id: int, tenant_id: int, role_id: int, **kwargs): 54 | """Connect a contect to a tenant 55 | 56 | :param contact_display_name: Contact display name 57 | :param tenant_id: The ID of the tenant 58 | :param kwargs: optional fields 59 | :return: netbox object if successful otherwise exception raised 60 | """ 61 | required_fields = { 62 | "content_type": "tenancy.tenant", 63 | "object_id": tenant_id, 64 | "contact": contact_id, 65 | "role": role_id 66 | } 67 | return self.netbox_con.post('/tenancy/contact-assignments/', required_fields, **kwargs) 68 | 69 | def delete_tenant(self, tenant_name): 70 | """Delete tenant 71 | 72 | :param tenant_name: Tenant to delete 73 | :return: bool True if succesful otherwise delete exception 74 | """ 75 | try: 76 | tenant_id = self.get_tenants(name=tenant_name)[0]['id'] 77 | except IndexError: 78 | raise exceptions.NotFoundException({"detail": "tenant: {}".format(tenant_name)}) from None 79 | return self.netbox_con.delete('/tenancy/tenants/', tenant_id) 80 | 81 | def delete_tenant_by_id(self, tenant_id): 82 | """Delete tenant 83 | 84 | :param tenant_id: Tenant to delete 85 | :return: bool True if succesful otherwise delete exception 86 | """ 87 | return self.netbox_con.delete('/tenancy/tenants/', tenant_id) 88 | 89 | def update_tenant(self, tenant_name, **kwargs): 90 | """Update tenant 91 | 92 | :param tenant_name: tenant to update 93 | :param kwargs: requests body dict 94 | :return: bool True if successful otherwise raise UpdateException 95 | """ 96 | try: 97 | tenant_id = self.get_tenants(name=tenant_name)[0]['id'] 98 | except IndexError: 99 | raise exceptions.NotFoundException({"detail": "tenant: {}".format(tenant_name)}) from None 100 | return self.netbox_con.patch('/tenancy/tenants/', tenant_id, **kwargs) 101 | 102 | def update_tenant_by_id(self, tenant_id, **kwargs): 103 | """Update tenant 104 | 105 | :param tenant_id: tenant to update 106 | :param kwargs: requests body dict 107 | :return: bool True if successful otherwise raise UpdateException 108 | """ 109 | return self.netbox_con.patch('/tenancy/tenants/', tenant_id, **kwargs) 110 | 111 | def get_tenant_groups(self, **kwargs): 112 | """Returns the tenant groups""" 113 | return self.netbox_con.get('/tenancy/tenant-groups/', **kwargs) 114 | 115 | def create_tenant_group(self, name, slug, **kwargs): 116 | """Create a new tenant-group 117 | 118 | :param name: Tenant-group name 119 | :param slug: slug name 120 | :param kwargs: optional fields 121 | :return: netbox object if successful otherwise exception raised 122 | """ 123 | required_fields = {"name": name, "slug": slug} 124 | return self.netbox_con.post('/tenancy/tenant-groups/', required_fields, **kwargs) 125 | 126 | def delete_tenant_group(self, tenant_group_name): 127 | """Delete tenant 128 | 129 | :param tenant_group_name: Tenant group to delete 130 | :return: bool True if succesful otherwise delete exception 131 | """ 132 | try: 133 | tenant_group_id = self.get_tenant_groups(name=tenant_group_name)[0]['id'] 134 | except IndexError: 135 | raise exceptions.NotFoundException({"detail": "tenant: {}".format(tenant_group_name)}) from None 136 | return self.netbox_con.delete('/tenancy/tenant-groups/', tenant_group_id) 137 | 138 | def delete_tenant_group_id(self, tenant_group_id): 139 | """Delete tenant 140 | 141 | :param tenant_group_id: Tenant group to delete 142 | :return: bool True if succesful otherwise delete exception 143 | """ 144 | return self.netbox_con.delete('/tenancy/tenant-groups/', tenant_group_id) 145 | 146 | def update_tenant_group(self, tenant_group_name, **kwargs): 147 | """Update tenant group 148 | 149 | :param tenant_group_name: tenant group to update 150 | :param kwargs: requests body dict 151 | :return: bool True if successful otherwise raise UpdateException 152 | """ 153 | try: 154 | tenant_group_id = self.get_tenant_groups(name=tenant_group_name)[0]['id'] 155 | except IndexError: 156 | raise exceptions.NotFoundException({"detail:": "tenant-group: {}".format(tenant_group_name)}) from None 157 | return self.netbox_con.patch('/tenancy/tenant-groups/', tenant_group_id, **kwargs) 158 | 159 | def update_tenant_group_by_id(self, tenant_group_id, **kwargs): 160 | """Update tenant group 161 | 162 | :param tenant_group_id: tenant group to update 163 | :param kwargs: requests body dict 164 | :return: bool True if successful otherwise raise UpdateException 165 | """ 166 | return self.netbox_con.patch('/tenancy/tenant-groups/', tenant_group_id, **kwargs) 167 | -------------------------------------------------------------------------------- /netbox/virtualization.py: -------------------------------------------------------------------------------- 1 | import netbox.exceptions as exceptions 2 | 3 | 4 | class Virtualization(object): 5 | 6 | def __init__(self, netbox_con): 7 | self.netbox_con = netbox_con 8 | 9 | def get_choices(self, choice_id=None): 10 | """Return choices for all fields if choice_id is not defined 11 | 12 | :param choice_id: Optional model:field tuple 13 | """ 14 | return self.netbox_con.get('/virtualization/_choices/', choice_id) 15 | 16 | def get_clusters(self, **kwargs): 17 | """Return all clusters""" 18 | return self.netbox_con.get('/virtualization/clusters/', **kwargs) 19 | 20 | def create_cluster(self, name, type, **kwargs): 21 | """Create a new cluster 22 | 23 | :param name: name of the cluster 24 | :param type: cluster type 25 | :param kwargs: optional arguments 26 | :return: netbox object if successful otherwise exception raised 27 | """ 28 | try: 29 | cluster_type_id = self.get_cluster_types(name=type)[0]['id'] 30 | except IndexError: 31 | raise exceptions.NotFoundException({"detail": "cluster-type: {}".format(name)}) from None 32 | 33 | required_fields = {"name": name, "type": cluster_type_id} 34 | return self.netbox_con.post('/virtualization/clusters/', required_fields, **kwargs) 35 | 36 | def delete_cluster(self, name): 37 | """Delete a cluster 38 | 39 | :param name: name of the cluster to delete 40 | :return: netbox object if succesful otherwise delete exception 41 | """ 42 | try: 43 | cluster_id = self.get_clusters(name=name)[0]['id'] 44 | except IndexError: 45 | raise exceptions.NotFoundException({"detail": "cluster {}".format(name)}) from None 46 | return self.netbox_con.delete('/virtualization/clusters/', cluster_id) 47 | 48 | def delete_cluster_by_id(self, cluster_id): 49 | """Delete a cluster 50 | 51 | :param cluster_id: cluster to delete 52 | :return: netbox object if succesful otherwise delete exception 53 | """ 54 | return self.netbox_con.delete('/virtualization/clusters/', cluster_id) 55 | 56 | def update_cluster(self, name, **kwargs): 57 | """Update cluster 58 | 59 | :param kwargs: filter arguments 60 | :param name: cluster name to update 61 | :return: bool True if successful otherwise raise UpdateException 62 | """ 63 | try: 64 | cluster_id = self.get_clusters(name=name)[0]['id'] 65 | except IndexError: 66 | raise exceptions.NotFoundException({"detail": "cluster: {}".format(name)}) from None 67 | return self.netbox_con.patch('/virtualization/clusters/', cluster_id, **kwargs) 68 | 69 | def update_cluster_by_id(self, cluster_id, **kwargs): 70 | """Update cluster 71 | 72 | :param kwargs: filter arguments 73 | :param cluster_id: cluster to update 74 | :return: bool True if successful otherwise raise UpdateException 75 | """ 76 | return self.netbox_con.patch('/virtualization/clusters/', cluster_id, **kwargs) 77 | 78 | def get_cluster_types(self, **kwargs): 79 | """Return all cluster types""" 80 | return self.netbox_con.get('/virtualization/cluster-types/', **kwargs) 81 | 82 | def create_cluster_type(self, name, slug): 83 | """Create a new cluster type 84 | 85 | :param name: name of the cluster 86 | :param slug: slug name 87 | :return: netbox object if successful otherwise create exception 88 | """ 89 | required_fields = {"name": name, "slug": slug} 90 | return self.netbox_con.post('/virtualization/cluster-types/', required_fields) 91 | 92 | def update_cluster_type(self, name, **kwargs): 93 | """Update cluster type 94 | 95 | :param name: name of the cluster type 96 | :param kwargs: fields to update 97 | :return: bool True if successful otherwise raise UpdateException 98 | """ 99 | try: 100 | type_id = self.get_cluster_types(name=name)[0]['id'] 101 | except IndexError: 102 | raise exceptions.NotFoundException({"detail": "cluster-type: {}".format(name)}) from None 103 | return self.netbox_con.patch('/virtualization/cluster-types/', type_id, **kwargs) 104 | 105 | def update_cluster_type_by_id(self, cluster_type_id, **kwargs): 106 | """Update cluster type 107 | 108 | :param cluster_type_id: ID of the cluster type 109 | :param kwargs: fields to update 110 | :return: bool True if successful otherwise raise UpdateException 111 | """ 112 | return self.netbox_con.patch('/virtualization/cluster-types/', cluster_type_id, **kwargs) 113 | 114 | def delete_cluster_type(self, name): 115 | """Delete a cluster type 116 | 117 | :param name: name of the cluster type to delete 118 | :return: bool True if succesful otherwise delete exception 119 | """ 120 | try: 121 | cluster_type_id = self.get_cluster_types(name=name)[0]['id'] 122 | except IndexError: 123 | raise exceptions.NotFoundException({"detail": "cluster-type: {}".format(name)}) from None 124 | return self.netbox_con.delete('/virtualization/cluster-types/', cluster_type_id) 125 | 126 | def delete_cluster_type_by_id(self, cluster_type_id): 127 | """Delete a cluster type 128 | 129 | :param cluster_type_id: cluster type to delete 130 | :return: bool True if succesful otherwise delete exception 131 | """ 132 | return self.netbox_con.delete('/virtualization/cluster-types/', cluster_type_id) 133 | 134 | def get_interfaces(self, **kwargs): 135 | """Return all interfaces""" 136 | return self.netbox_con.get('/virtualization/interfaces/', **kwargs) 137 | 138 | def get_interface(self, **kwargs): 139 | """Return interface by filter""" 140 | return self.netbox_con.get('/virtualization/interfaces/', **kwargs) 141 | 142 | def create_interface(self, name, virtual_machine, **kwargs): 143 | """Create an interface for a virtual machine 144 | 145 | :param name: name of the interface 146 | :param virtual_machine: name of virtual machine to attach interface 147 | :return: netbox object if successful otherwise raise CreateException 148 | """ 149 | try: 150 | virtual_machine_id = self.get_virtual_machine(name=virtual_machine)[0]['id'] 151 | except IndexError: 152 | raise exceptions.NotFoundException({"detail": "virtual-machine: {}".format(virtual_machine)}) from None 153 | required_fields = {"name": name, "virtual_machine": virtual_machine_id} 154 | return self.netbox_con.post('/virtualization/interfaces/', required_fields, **kwargs) 155 | 156 | def update_interface(self, name, virtual_machine, **kwargs): 157 | """Update virtual_machine interface 158 | 159 | :param name: name of the interface to update 160 | :param virtual_machine: name of the virtual-machine to update 161 | :param kwargs: update data 162 | :return: bool True if successful otherwise raise UpdateException 163 | """ 164 | try: 165 | interface_id = self.get_interface(name=name, virtual_machine=virtual_machine)[0]['id'] 166 | except IndexError: 167 | raise exceptions.NotFoundException({"detail": "interface: {} or virtual_machine: {}" 168 | .format(name, virtual_machine)}) from None 169 | return self.netbox_con.patch('/virtualization/interfaces/', interface_id, **kwargs) 170 | 171 | def update_interface_by_id(self, interface_id, **kwargs): 172 | """Update virtual_machine interface 173 | 174 | :param interface_id: id the interface to update 175 | :param kwargs: update data 176 | :return: bool True if successful otherwise raise UpdateException 177 | """ 178 | return self.netbox_con.patch('/virtualization/interfaces/', interface_id, **kwargs) 179 | 180 | def delete_interface(self, name, virtual_machine): 181 | """Delete interface from virtual_machine 182 | 183 | :param name: interface name to delete 184 | :param virtual_machine: machine to delete interface from 185 | :return: bool True if successful otherwise raise DeleteException 186 | """ 187 | try: 188 | interface_id = self.get_interface(name=name, virtual_machine=virtual_machine)[0]['id'] 189 | except IndexError: 190 | raise exceptions.NotFoundException({"detail": "interface: {} or virtual_machine {}" 191 | .format(name, virtual_machine)}) from None 192 | 193 | return self.netbox_con.delete('/virtualization/interfaces/', interface_id) 194 | 195 | def delete_interface_by_id(self, interface_id): 196 | """Delete interface from virtual_machine 197 | 198 | :param interface_id: interface to delete 199 | :return: bool True if successful otherwise raise DeleteException 200 | """ 201 | return self.netbox_con.delete('/virtualization/interfaces/', interface_id) 202 | 203 | def get_virtual_machines(self, **kwargs): 204 | """Return all virtual-machines""" 205 | return self.netbox_con.get('/virtualization/virtual-machines/', **kwargs) 206 | 207 | def get_virtual_machine(self, **kwargs): 208 | """Return virtual-machine based on filter""" 209 | return self.netbox_con.get('/virtualization/virtual-machines/', **kwargs) 210 | 211 | def create_virtual_machine(self, name, cluster_name, **kwargs): 212 | """Create a virtual machine 213 | 214 | :param name: name of the virtual machine 215 | :param cluster_name: Name of existing cluster 216 | :return: netbox object if successful otherwise raise CreateException 217 | """ 218 | try: 219 | cluster_id = self.get_clusters(name=cluster_name)[0]['id'] 220 | except IndexError: 221 | raise exceptions.NotFoundException({"detail": "cluster-name: {}".format(cluster_name)}) from None 222 | 223 | required_fields = {"name": name, "cluster": cluster_id} 224 | return self.netbox_con.post('/virtualization/virtual-machines/', required_fields, **kwargs) 225 | 226 | def delete_virtual_machine(self, virtual_machine_name): 227 | """Delete virtual machine 228 | 229 | :param virtual_machine_name: name of the virtual machine to delete 230 | :return: bool True if successful otherwise raise exception 231 | """ 232 | try: 233 | virtual_machine_id = self.get_virtual_machines(name=virtual_machine_name)[0]['id'] 234 | except IndexError: 235 | raise exceptions.NotFoundException({"detail": "virtual-machine: {}".format(virtual_machine_name)}) from None 236 | return self.netbox_con.delete('/virtualization/virtual-machines/', virtual_machine_id) 237 | 238 | def delete_virtual_machine_by_id(self, virtual_machine_id): 239 | """Delete virtual machine 240 | 241 | :param virtual_machine_id: virtual machine to delete 242 | :return: bool True if successful otherwise raise exception 243 | """ 244 | return self.netbox_con.delete('/virtualization/virtual-machines/', virtual_machine_id) 245 | 246 | def update_virtual_machine(self, virtual_machine_name, **kwargs): 247 | """Update virtual-machine 248 | 249 | :param virtual_machine_name: name of the virtual-machine to update 250 | :param kwargs: update data 251 | :return: bool True if successful otherwise raise UpdateException 252 | """ 253 | try: 254 | virtual_machine_id = self.get_virtual_machines(name=virtual_machine_name)[0]['id'] 255 | except IndexError: 256 | raise exceptions.NotFoundException({"detail": "virtual-machine: {}" 257 | .format(virtual_machine_name)}) from None 258 | return self.netbox_con.patch('/virtualization/virtual-machines/', virtual_machine_id, **kwargs) 259 | 260 | def update_virtual_machine_by_id(self, virtual_machine_id, **kwargs): 261 | """Update virtual-machine 262 | 263 | :param virtual_machine_id: virtual-machine to update 264 | :param kwargs: update data 265 | :return: bool True if successful otherwise raise UpdateException 266 | """ 267 | return self.netbox_con.patch('/virtualization/virtual-machines/', virtual_machine_id, **kwargs) 268 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | ipaddress 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | from setuptools import setup, find_packages 5 | 6 | 7 | def read(fname): 8 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 9 | 10 | 11 | def readme(): 12 | with open('README.rst') as f: 13 | return f.read() 14 | 15 | 16 | setup(name='python-netbox', 17 | version='0.0.24', 18 | description='Python NetBox Client', 19 | long_description=readme(), 20 | python_requires='>=3', 21 | author='Thomas van der Jagt', 22 | author_email='thomas@tjrb.nl', 23 | url='https://github.com/jagter/python-netbox', 24 | download_url='https://github.com/jagter/python-netbox/releases/tag/0.0.24.tar.gz', 25 | packages=find_packages(), 26 | install_requires=['ipaddress', 'requests'], 27 | classifiers = [ 28 | "Programming Language :: Python :: 3", 29 | "Intended Audience :: System Administrators", 30 | "License :: OSI Approved :: Apache Software License", 31 | "Programming Language :: Python :: 3.6", 32 | ], 33 | ) 34 | -------------------------------------------------------------------------------- /test_requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | ipaddress 3 | unittest --------------------------------------------------------------------------------