diff options
-rw-r--r-- | CONTRIB.md | 10 | ||||
-rw-r--r-- | docs/source/conf.py | 6 | ||||
-rw-r--r-- | poetry.lock | 32 | ||||
-rw-r--r-- | pyproject.toml | 12 | ||||
-rw-r--r-- | pyvyos/device.py | 94 | ||||
-rw-r--r-- | requirements.txt | 3 | ||||
-rw-r--r-- | vagrant/.env.example | 13 | ||||
-rw-r--r-- | vagrant/VAGRANT.md | 35 | ||||
-rw-r--r-- | vagrant/Vagrantfile | 50 |
9 files changed, 161 insertions, 94 deletions
@@ -1,6 +1,6 @@ -# Contributing to PyVyOS +# Contributing to pyvyos -We welcome contributions to the PyVyOS project! If you're looking to contribute, please take a moment to read this guide to understand how you can be a part of our community and help make PyVyOS better. +We welcome contributions to the pyvyos project! If you're looking to contribute, please take a moment to read this guide to understand how you can be a part of our community and help make pyvyos better. ## Code of Conduct @@ -8,7 +8,7 @@ We expect all contributors to adhere to our Code of Conduct. Respectful, collabo ## How to Contribute -There are many ways to contribute to PyVyOS: +There are many ways to contribute to pyvyos: - **Submitting bug reports and feature requests:** Use our issue tracker to report bugs or suggest features. - **Writing code:** Feel free to take on open issues or propose new changes. Please follow the guidelines outlined below. @@ -61,7 +61,7 @@ There are many ways to contribute to PyVyOS: ## License -By contributing to PyVyOS, you agree that your contributions will be licensed under the same license that covers the project. +By contributing to pyvyos, you agree that your contributions will be licensed under the same license that covers the project. -Thank you for considering contributing to PyVyOS. Your efforts are what make this project great! +Thank you for considering contributing to pyvyos. Your efforts are what make this project great! diff --git a/docs/source/conf.py b/docs/source/conf.py index 8e9f3c2..4b3b786 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,10 +6,10 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -project = 'PyVyOS' -copyright = '2023, Roberto Berto' +project = 'pyvyos' +copyright = '2024, Roberto Berto' author = 'Roberto Berto' -release = '0.2.0' +release = '0.2.1' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/poetry.lock b/poetry.lock index 7f20ce3..ab3c950 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,14 +1,14 @@ -# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "certifi" -version = "2023.11.17" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] @@ -112,24 +112,24 @@ files = [ [[package]] name = "idna" -version = "3.4" +version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[package]] name = "python-dotenv" -version = "1.0.0" +version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, ] [package.extras] @@ -158,22 +158,22 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "urllib3" -version = "2.0.7" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, - {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = ">=3.8" -content-hash = "ef09cfdf5c9430601a5f2044d458baa6184b53a705a76f9ef1c4bd7c00a598bb" +content-hash = "f223c5fdd1d6f40fab9f9dc2320a2ee4aab1f2a82eea8a9b020e63621a887845" diff --git a/pyproject.toml b/pyproject.toml index 11613a0..e90621b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyvyos" -version = "0.2.0" +version = "0.2.1" authors = [ { name="Roberto Berto", email="463349+robertoberto@users.noreply.github.com" }, ] @@ -18,13 +18,13 @@ classifiers = [ ] [project.urls] -Homepage = "https://github.com/gravscale/pyvyos" -Issues = "https://github.com/gravscale/pyvyos/issues" +Homepage = "https://github.com/robertoberto/pyvyos" +Issues = "https://github.com/robertoberto/pyvyos/issues" [tool.hatch.metadata] dependencies = [ "requests>=2.31.0,<3.0", - "python-dotenv>=1.0.0,<2.0" + "python-dotenv>=1.0.1,<2.0" ] dev-dependencies = [ "pytest>=6.2.2,<7.0" @@ -34,10 +34,10 @@ dev-dependencies = [ name = "pyvyos" description = "Python SDK for interacting with VyOS API" authors = ["Roberto Berto <463349+robertoberto@users.noreply.github.com>"] -version = "0.2.0" +version = "0.2.1" [tool.poetry.dependencies] python = ">=3.8" requests = "^2.31.0" -python-dotenv = "^1.0.0" +python-dotenv = "^1.0.1" diff --git a/pyvyos/device.py b/pyvyos/device.py index 6d8a1dc..d168b65 100644 --- a/pyvyos/device.py +++ b/pyvyos/device.py @@ -52,8 +52,8 @@ class VyDevice: show(path=[]): Show configuration information. generate(path=[]): Generate configuration based on specified path. configure_set(path=[]): Sets configuration based on the specified path. This method is versatile, accepting - either a single configuration path or a list of configuration paths. This flexibility - allows for setting both individual and multiple configurations in a single operation. + either a single configuration path or a list of configuration paths. This flexibility + allows for setting both individual and multiple configurations in a single operation. configure_delete(path=[]): Delete configuration based on specified path. config_file_save(file=None): Save the configuration to a file. config_file_load(file=None): Load the configuration from a file. @@ -98,7 +98,8 @@ class VyDevice: Args: op (str): The operation to perform in the API request. - path (list, optional): The path elements for the API request (default is an empty list). + path (list, optional): The path elements for the API request. This can be a single list for a single + configuration path or a list of lists for multiple configuration paths. file (str, optional): The file to include in the request (default is None). url (str, optional): The URL to include in the request (default is None). name (str, optional): The name to include in the request (default is None). @@ -106,64 +107,42 @@ class VyDevice: Returns: dict: The payload for the API request. """ - if not path: - data = { - 'op': op, - 'path': path - } - - if file is not None: + # Adjusting the data structure based on whether path is single or multiple + if isinstance(path[0], list): # Handling multiple paths + data = [{'op': op, 'path': p} for p in path] + else: # Handling a single path + data = {'op': op, 'path': path} + + # Including the optional parameters if provided + if file: + if isinstance(data, list): # If data is a list of dicts (multiple paths) + for d in data: + d['file'] = file + else: # If data is a single dict (single path) data['file'] = file - if url is not None: + if url: + if isinstance(data, list): + for d in data: + d['url'] = url + else: data['url'] = url - if name is not None: + if name: + if isinstance(data, list): + for d in data: + d['name'] = name + else: data['name'] = name - payload = { - 'data': json.dumps(data), - 'key': self.apikey - } - - return payload - - elif isinstance(path, list) and len(path) == 1: - # If path is a list and contains only one element, use it directly - data = {'op': op, 'path': path[0]} - else: - data = [] - current_command = {'op': op, 'path': []} - for p in path: - if isinstance(p, list): - # If the current item is a list, merge it into the current command - if current_command['path']: - data.append(current_command) - current_command = {'op': op, 'path': p} - else: - # Otherwise, add the item to the current command's path - current_command['path'].append(p) - - # Add the last command to data - if current_command['path']: - data.append(current_command) - payload = { 'data': json.dumps(data), 'key': self.apikey } - if file is not None: - data['file'] = file - - if url is not None: - payload['url'] = url - - if name is not None: - data['name'] = name - return payload + def _api_request(self, command, op, path=[], method='POST', file=None, url=None, name=None): """ Make an API request. @@ -308,24 +287,13 @@ class VyDevice: """ Set configuration based on the given path. - This method accepts both a single configuration path as a list and a list of configuration paths for setting multiple configurations in one go. - Args: - path (list): The path elements for configuration setting. This can be a list for a single configuration, - or a list of lists for multiple configurations. + path (list, optional): The path elements for configuration setting (default is an empty list). Returns: - ApiResponse or list of ApiResponse: Returns a single ApiResponse object for a single configuration, or a list of ApiResponse objects for multiple configurations. - """ - # Check if the first element of the path is a list, indicating multiple configurations - if path and isinstance(path[0], list): - # Process each configuration path separately - responses = [self._api_request("configure", "set", [p], "POST") for p in path] - # Return a list of ApiResponse objects for multiple configurations - return responses - else: - # Handle a single configuration path - return self._api_request("configure", "set", path, "POST") + ApiResponse: An ApiResponse object representing the API response. + """ + return self._api_request(command="configure", op='set', path=path, method="POST") def configure_delete(self, path=[]): diff --git a/requirements.txt b/requirements.txt index 523e2c2..56dc8de 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ requests>=2.25.1,<3.0 -python-dotenv>=1.0.0,<2.0 +python-dotenv>=1.0.1,<2.0 +poetry>=1.1.0,<2.0
\ No newline at end of file diff --git a/vagrant/.env.example b/vagrant/.env.example new file mode 100644 index 0000000..36bebf8 --- /dev/null +++ b/vagrant/.env.example @@ -0,0 +1,13 @@ + +# vyos https api key +VYDEVICE_APIKEY=your_api_key + +# vyos network interface +VYDEVICE_IP=192.168.56.10 +VYDEVICE_NETMASK=255.255.255.0 + +# HOST_IP is the ip address of the host machine of the virtualbox +# use your windows network interface for virtualbox running on windows host and using wsl2 for vagrant +HOST_IP=192.168.0.114 +# or use 127.0.0.1, virtualbox running on linux host or windows host +# HOST_IP=127.0.0.1
\ No newline at end of file diff --git a/vagrant/VAGRANT.md b/vagrant/VAGRANT.md new file mode 100644 index 0000000..fab5e59 --- /dev/null +++ b/vagrant/VAGRANT.md @@ -0,0 +1,35 @@ +# Vagrant only for development and tests + +Vagrant is a tool for building and managing virtual machine environments +in pyvyos we use vagrant to deploy vyos virtual machines +for development and automated tests + +If you want to only use pyvyos you dont need to install vagrant + +# Vagrant install instructions + +1. Install Vagrant +2. Install VirtualBox +3. Install Vagrant plugin for vyos +``` +vagrant plugin install vagrant-vyos +``` +4. Install mkisofs +``` +sudo apt install genisoimage +``` + +5. Run vagrant up +``` +vagrant up +``` +6. Run vagrant ssh +``` +vagrant ssh +``` + +# For Windows with wsl2: +``` +export VAGRANT_WSL_ENABLE_WINDOWS_ACCESS="1" +export PATH="$PATH:/mnt/c/Program Files/Oracle/VirtualBox" +```
\ No newline at end of file diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile new file mode 100644 index 0000000..89edbf3 --- /dev/null +++ b/vagrant/Vagrantfile @@ -0,0 +1,50 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Documentation: +# - read VAGRANT.md +# - need vagrant plugin install vagrant-vyos + +Vagrant.configure("2") do |config| + # enable the environment variables + # need vagrant plugin install vagrant-dotenv + config.env.enable + + # vm to deploy tests + config.vm.define "pyvyos" do |pyvyos| + pyvyos.vm.box = "vyos/current" + pyvyos.vm.hostname = "pyvyos" + + # network configuration of eth1 + pyvyos.vm.network "private_network", ip: ENV['VYDEVICE_IP'], netmask: ENV['VYDEVICE_NETMASK'] + pyvyos.ssh.host = ENV['HOST_IP'] + + # nat port forwarding + pyvyos.vm.network "forwarded_port", guest: 443, host: 8433, id: "https", auto_correct: true, protocol: "tcp", host_ip: ENV['HOST_IP'] + pyvyos.vm.network "forwarded_port", guest: 22, host: 2022, id: "ssh", auto_correct: true, protocol: "tcp", host_ip: ENV['HOST_IP'] + + # ssh configuration default username and password of vyos/current is vyos / vyos + # if you want to change the default password, you can change in provision script + # also, you can disable ssh password in provision script and use only ssh key + pyvyos.ssh.username = "vyos" + pyvyos.ssh.password = "vyos" + + # vagrant will insert the ssh key in the vm automatically, so password authentication after + # first boot is not necessary + pyvyos.ssh.insert_key = true + + # shell script to provision the vyos vm + pyvyos.vm.provision "shell", inline: <<-SHELL + #!/bin/vbash + source /opt/vyatta/etc/functions/script-template + configure + set service https listen-address '#{ENV['VYDEVICE_IP']}' + set service https api keys id 'apikey' key '#{ENV['VYDEVICE_APIKEY']}' + set service https api debug + set service https api strict + commit + save + exit + SHELL + end + end |