diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml new file mode 100644 index 0000000..2cc7f8c --- /dev/null +++ b/.github/workflows/pypi.yml @@ -0,0 +1,80 @@ +name: Publish Python distribution to PyPI and TestPyPI + +on: + pull_request: + branches: [ "main" ] + push: + branches: [ "main", "dev" ] + tags: [ "v*" ] + +jobs: + build: + name: Build distribution + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.x" + - name: Install pypa/build + run: >- + python3 -m + pip install + build + --user + - name: Build a binary wheel and a source tarball + run: make build + - name: Store the distribution packages + uses: actions/upload-artifact@v3 + with: + name: python-package-distributions + path: dist/ + + publish-to-testpypi: + name: Publish to TestPyPI + needs: + - build + runs-on: ubuntu-latest + + environment: + name: devtest + url: https://test.pypi.org/p/nrx + + permissions: + id-token: write # IMPORTANT: mandatory for trusted publishing + + steps: + - name: Download all the dists + uses: actions/download-artifact@v3 + with: + name: python-package-distributions + path: dist/ + - name: Publish distribution to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + + publish-to-pypi: + name: Publish to PyPI + if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes + needs: + - build + runs-on: ubuntu-latest + + environment: + name: release + url: https://pypi.org/p/nrx + + permissions: + id-token: write # IMPORTANT: mandatory for trusted publishing + + steps: + - name: Download all the dists + uses: actions/download-artifact@v3 + with: + name: python-package-distributions + path: dist/ + - name: Publish distribution to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index 843bed1..a54448d 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,4 @@ tests/*/d2/* tests/main.conf tmp demo +dist \ No newline at end of file diff --git a/Makefile b/Makefile index ee00aca..29e4f66 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,17 @@ lint: - pylint nrx/*.py + pylint src/nrx/*.py + +build: + python3 -m build + +pubdev: + python3 -m twine upload --repository testpypi dist/* + +publish: + python3 -m twine upload dist/* + +clean: + rm dist/* test-local: test-dc1 test-dc2 test-colo test-site1 test-h88 test-local-lrg: test-lrg-nb-2-cyjs-latest @@ -24,7 +36,7 @@ test-args-site-and-sites: @echo "#################################################################" @echo "# Simulteneous use of site and sites should fail" @echo "#################################################################" - ! ./nrx.py --site dc1 --sites dc1,dc2 -d + ! ./nrx --site dc1 --sites dc1,dc2 -d @echo test-dc1-nb-2-cyjs-current: @@ -33,7 +45,7 @@ test-dc1-nb-2-cyjs-current: @echo "#################################################################" mkdir -p tests/dc1/test && cd tests/dc1/test && rm -rf * && \ source ../../.env_current && \ - ../../../nrx.py -c ../nrx.conf -o cyjs -d && \ + ../../../nrx -c ../nrx.conf -o cyjs -d && \ diff dc1.cyjs ../data/dc1.cyjs @echo @@ -43,7 +55,7 @@ test-dc1-nb-2-cyjs-latest: @echo "#################################################################" mkdir -p tests/dc1/test && cd tests/dc1/test && rm -rf * && \ source ../../.env_latest && \ - ../../../nrx.py -c ../nrx.conf -o cyjs -d && \ + ../../../nrx -c ../nrx.conf -o cyjs -d && \ diff dc1.cyjs ../data/dc1.cyjs @echo @@ -53,7 +65,7 @@ test-dc1-nb-2-cyjs-single-site: @echo "#################################################################" mkdir -p tests/dc1/test && cd tests/dc1/test && rm -rf * && \ source ../../.env_current && \ - ../../../nrx.py -c ../nrx-no-site.conf -o cyjs --site dc1 -d && \ + ../../../nrx -c ../nrx-no-site.conf -o cyjs --site dc1 -d && \ diff dc1.cyjs ../data/dc1.cyjs @echo @@ -63,7 +75,7 @@ test-dc1-nb-2-cyjs-single-sites: @echo "#################################################################" mkdir -p tests/dc1/test && cd tests/dc1/test && rm -rf * && \ source ../../.env_current && \ - ../../../nrx.py -c ../nrx-no-site.conf -o cyjs --sites dc1 -d && \ + ../../../nrx -c ../nrx-no-site.conf -o cyjs --sites dc1 -d && \ diff dc1.cyjs ../data/dc1.cyjs @echo @@ -73,7 +85,7 @@ test-dc1-dc2-nb-2-cyjs-sites: @echo "#################################################################" mkdir -p tests/dc1/test && cd tests/dc1/test && rm -rf * && \ source ../../.env_current && \ - ../../../nrx.py -c ../nrx-no-site.conf -o cyjs --sites dc1,dc2 -d && \ + ../../../nrx -c ../nrx-no-site.conf -o cyjs --sites dc1,dc2 -d && \ diff dc1-dc2.cyjs ../data/dc1-dc2.cyjs @echo @@ -82,7 +94,7 @@ test-dc1-cyjs-2-clab: @echo "# DC1: read from CYJS and export as Containerlab" @echo "#################################################################" mkdir -p tests/dc1/test && cd tests/dc1/test && rm -rf * && \ - ../../../nrx.py -c ../nrx.conf -i cyjs -f ../data/dc1.cyjs -d && \ + ../../../nrx -c ../nrx.conf -i cyjs -f ../data/dc1.cyjs -d && \ for f in *; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done @echo @@ -91,7 +103,7 @@ test-dc1-cyjs-2-clab-custom-platform-map: @echo "# DC1: read from CYJS and export as Containerlab using custom platform map" @echo "#################################################################" mkdir -p tests/dc1/test && cd tests/dc1/test && rm -rf * && \ - ../../../nrx.py -c ../nrx.conf -i cyjs -f ../data/dc1.cyjs -M ../platform_map.yaml -d && \ + ../../../nrx -c ../nrx.conf -i cyjs -f ../data/dc1.cyjs -M ../platform_map.yaml -d && \ for f in *; do echo Comparing file $$f ...; diff $$f ../custom-clab/$$f || exit 1; done @echo @@ -100,7 +112,7 @@ test-dc1-cyjs-2-graphite: @echo "# DC1: read from CYJS and export as graphite" @echo "#################################################################" mkdir -p tests/dc1/graphite && cd tests/dc1/graphite && rm -rf * && \ - ../../../nrx.py -c ../nrx.conf -i cyjs -f ../data/dc1.cyjs -o graphite -d && \ + ../../../nrx -c ../nrx.conf -i cyjs -f ../data/dc1.cyjs -o graphite -d && \ for f in *; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done @echo @@ -109,7 +121,7 @@ test-dc1-dc2-cyjs-2-graphite: @echo "# DC1 and DC2: read from CYJS and export as graphite" @echo "#################################################################" mkdir -p tests/dc1/graphite && cd tests/dc1/graphite && rm -rf * && \ - ../../../nrx.py -c ../nrx-no-site.conf -i cyjs -f ../data/dc1-dc2.cyjs -o graphite -d && \ + ../../../nrx -c ../nrx-no-site.conf -i cyjs -f ../data/dc1-dc2.cyjs -o graphite -d && \ for f in *; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done @echo @@ -118,7 +130,7 @@ test-dc1-cyjs-2-d2: @echo "# DC1: read from CYJS and export as d2" @echo "#################################################################" mkdir -p tests/dc1/d2 && cd tests/dc1/d2 && rm -rf * && \ - ../../../nrx.py -c ../nrx.conf -i cyjs -f ../data/dc1.cyjs -o d2 -d && \ + ../../../nrx -c ../nrx.conf -i cyjs -f ../data/dc1.cyjs -o d2 -d && \ for f in *; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done @echo @@ -136,7 +148,7 @@ test-dc2-nb-2-cyjs-current: @echo "#################################################################" mkdir -p tests/dc2/test && cd tests/dc2/test && rm -rf * && \ source ../../.env_current && \ - ../../../nrx.py -c ../nrx.conf -o cyjs -d && \ + ../../../nrx -c ../nrx.conf -o cyjs -d && \ diff dc2.cyjs ../data/dc2.cyjs.current @echo @@ -146,7 +158,7 @@ test-dc2-nb-2-cyjs-latest: @echo "#################################################################" mkdir -p tests/dc2/test && cd tests/dc2/test && rm -rf * && \ source ../../.env_latest && \ - ../../../nrx.py -c ../nrx.conf -o cyjs -d && \ + ../../../nrx -c ../nrx.conf -o cyjs -d && \ diff dc2.cyjs ../data/dc2.cyjs.latest @echo @@ -155,7 +167,7 @@ test-dc2-cyjs-2-graphite: @echo "# DC2: read from CYJS and export as graphite" @echo "#################################################################" mkdir -p tests/dc2/graphite && cd tests/dc2/graphite && rm -rf * && \ - ../../../nrx.py -c ../nrx.conf -i cyjs -f ../data/dc2.cyjs -o graphite -d && \ + ../../../nrx -c ../nrx.conf -i cyjs -f ../data/dc2.cyjs -o graphite -d && \ for f in *; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done @echo @@ -164,7 +176,7 @@ test-dc2-cyjs-2-cml: @echo "# DC2: read from CYJS and export as CML" @echo "#################################################################" mkdir -p tests/dc2/test && cd tests/dc2/test && rm -rf * && \ - ../../../nrx.py -c ../nrx.conf -i cyjs -f ../data/dc2.cyjs -d && \ + ../../../nrx -c ../nrx.conf -i cyjs -f ../data/dc2.cyjs -d && \ for f in *; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done @echo @@ -182,7 +194,7 @@ test-colo-nb-2-cyjs-current: @echo "#################################################################" mkdir -p tests/colo/test && cd tests/colo/test && rm -rf * && \ source ../../.env_current && \ - ../../../nrx.py -c ../nrx.conf -o cyjs -d && \ + ../../../nrx -c ../nrx.conf -o cyjs -d && \ diff colo.cyjs ../data/colo.cyjs @echo @@ -192,7 +204,7 @@ test-colo-nb-2-cyjs-latest: @echo "#################################################################" mkdir -p tests/colo/test && cd tests/colo/test && rm -rf * && \ source ../../.env_latest && \ - ../../../nrx.py -c ../nrx.conf -o cyjs -d && \ + ../../../nrx -c ../nrx.conf -o cyjs -d && \ diff colo.cyjs ../data/colo.cyjs @echo @@ -202,7 +214,7 @@ test-site1-nb-2-cyjs-current: @echo "#################################################################" mkdir -p tests/site1/test && cd tests/site1/test && rm -rf * && \ source ../../.env_current && \ - ../../../nrx.py -c ../nrx.conf -o cyjs -d && \ + ../../../nrx -c ../nrx.conf -o cyjs -d && \ diff site1.cyjs ../data/site1.cyjs @echo @@ -212,7 +224,7 @@ test-site1-nb-2-cyjs-latest: @echo "#################################################################" mkdir -p tests/site1/test && cd tests/site1/test && rm -rf * && \ source ../../.env_latest && \ - ../../../nrx.py -c ../nrx.conf -o cyjs -d && \ + ../../../nrx -c ../nrx.conf -o cyjs -d && \ diff site1.cyjs ../data/site1.cyjs @echo @@ -221,7 +233,7 @@ test-site1-cyjs-2-clab: @echo "# Site1: read from CYJS and export as Containerlab" @echo "#################################################################" mkdir -p tests/site1/test && cd tests/site1/test && rm -rf * && \ - ../../../nrx.py -c ../nrx.conf -i cyjs -f ../data/site1.cyjs -o clab -d && \ + ../../../nrx -c ../nrx.conf -i cyjs -f ../data/site1.cyjs -o clab -d && \ for f in *; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done @echo @@ -230,7 +242,7 @@ test-site1-cyjs-2-clab-rename: @echo "# Site1: read from CYJS and export as Containerlab with a custom name" @echo "#################################################################" mkdir -p tests/site1/test && cd tests/site1/test && rm -rf * && \ - ../../../nrx.py -c ../nrx.conf -i cyjs -f ../data/site1.cyjs -o clab --name ABC -d && \ + ../../../nrx -c ../nrx.conf -i cyjs -f ../data/site1.cyjs -o clab --name ABC -d && \ for f in *; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done @echo @@ -240,7 +252,7 @@ test-site1-cyjs-template-2-clab: @echo "#################################################################" mkdir -p tests/site1/test && cd tests/site1/test && rm -rf * && \ cat ../data/site1.cyjs.template | envsubst > site1.sonic-vs.cyjs && \ - ../../../nrx.py -c ../nrx.conf -i cyjs -f site1.sonic-vs.cyjs -o clab -d && \ + ../../../nrx -c ../nrx.conf -i cyjs -f site1.sonic-vs.cyjs -o clab -d && \ for f in *.yaml; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done @echo @@ -251,7 +263,7 @@ test-h88-nb-2-cyjs-current: @echo "#################################################################" mkdir -p tests/h88/test && cd tests/h88/test && rm -rf * && \ source ../../.env_current && \ - ../../../nrx.py -c ../nrx.conf -o cyjs -d && \ + ../../../nrx -c ../nrx.conf -o cyjs -d && \ diff HQ.cyjs ../data/HQ.cyjs.current @echo @@ -261,7 +273,7 @@ test-h88-nb-2-cyjs-latest: @echo "#################################################################" mkdir -p tests/h88/test && cd tests/h88/test && rm -rf * && \ source ../../.env_latest && \ - ../../../nrx.py -c ../nrx.conf -o cyjs -d && \ + ../../../nrx -c ../nrx.conf -o cyjs -d && \ diff HQ.cyjs ../data/HQ.cyjs.latest @echo @@ -271,7 +283,7 @@ test-h88-nb-2-cyjs-latest-noconfigs: @echo "#################################################################" mkdir -p tests/h88/test && cd tests/h88/test && rm -rf * && \ source ../../.env_latest && \ - ../../../nrx.py -c ../nrx.conf -o cyjs -d --noconfigs && \ + ../../../nrx -c ../nrx.conf -o cyjs -d --noconfigs && \ diff HQ.cyjs ../data/HQ.cyjs.noconfigs @echo @@ -280,7 +292,7 @@ test-h88-cyjs-2-clab: @echo "# h88: read from CYJS and export as Containerlab" @echo "#################################################################" mkdir -p tests/h88/test && cd tests/h88/test && rm -rf * && \ - ../../../nrx.py -c ../nrx.conf -i cyjs -f ../data/HQ.cyjs -o clab -d && \ + ../../../nrx -c ../nrx.conf -i cyjs -f ../data/HQ.cyjs -o clab -d && \ for f in *; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done @echo @@ -298,7 +310,7 @@ test-lrg-nb-2-cyjs-latest: @echo "#################################################################" mkdir -p tests/lrg/test && cd tests/lrg/test && rm -rf * && \ source ../../.env_latest && \ - ../../../nrx.py -c ../nrx.conf -o cyjs --noconfigs -d && \ + ../../../nrx -c ../nrx.conf -o cyjs --noconfigs -d && \ diff lrg.cyjs ../data/lrg.cyjs @echo @@ -307,7 +319,7 @@ test-lrg-cyjs-2-graphite: @echo "# LRG: read from CYJS and export as graphite" @echo "#################################################################" mkdir -p tests/lrg/graphite && cd tests/lrg/graphite && rm -rf * && \ - ../../../nrx.py -c ../nrx.conf -i cyjs -f ../data/lrg.cyjs -o graphite -d && \ + ../../../nrx -c ../nrx.conf -i cyjs -f ../data/lrg.cyjs -o graphite -d && \ for f in *; do echo Comparing file $$f ...; diff $$f ../data/$$f || exit 1; done @echo diff --git a/README.md b/README.md index bbe2a85..303a250 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,11 @@ This project is in early phase. We're experimenting with the best ways to automa # Latest capabilities added -The latest release has a significant set of the new capabilities: -* Ability to create new output formats without a need for **nrx** code changes -* Mapping between NetBox platform values and node parameters via [`platform_map.yaml`](docs/platform_map.md) file -* `$HOME/.nr` configuration directory with automatic initialization using `--init` argument +The latest releases have a significant set of the new capabilities: +* `0.5.0` PyPA packaging and distribution: `pip install nrx` +* `0.4.0` Ability to create new output formats without a need for **nrx** code changes +* `0.4.0` Mapping between NetBox platform values and node parameters via [`platform_map.yaml`](docs/platform_map.md) file +* `0.4.0` `$HOME/.nr` configuration directory with automatic initialization using `--init` argument Find detailed release notes on the [Releases page](https://github.com/netreplica/nrx/releases). @@ -102,20 +103,26 @@ The following software versions were tested for compatibility with `nrx`: # How to install -1. Clone this repository and create Python virtual environment +## PyPI package (recommended) - ```Shell - git clone https://github.com/netreplica/nrx.git --recursive - cd nrx - python3.9 -m venv nrx39 - source nrx39/bin/activate - ``` +```Shell +mkdir -p ~/.venv +python3.9 -m venv ~/.venv/nrx +source ~/.venv/nrx/bin/activate +pip install nrx +``` -2. Install required modules +## From source code (development) - ```Shell - pip3 install -r requirements.txt - ``` +After running the following commands, you will have a working `nrx` command in the current directory. + +```Shell +git clone https://github.com/netreplica/nrx.git --recursive +cd nrx +python3.9 -m venv venv +source venv/bin/activate +pip install -r requirements.txt +``` # How to configure @@ -130,8 +137,8 @@ The following software versions were tested for compatibility with `nrx`: Command-line arguments take the highest priority. ``` -./nrx.py --help -usage: nrx [-h] [-v] [-d] [-I] [-c CONFIG] [-i INPUT] [-o OUTPUT] [-a API] [-s SITE] [-t TAGS] [-n NAME] +nrx --help +usage: nrx [-h] [-v] [-d] [-I [VERSION]] [-c CONFIG] [-i INPUT] [-o OUTPUT] [-a API] [-s SITE] [-t TAGS] [-n NAME] [--noconfigs] [-k | --insecure] [-f FILE] [-M MAP] [-T TEMPLATES] [-D DIR] nrx - network topology exporter by netreplica @@ -140,7 +147,7 @@ optional arguments: -h, --help show this help message and exit -v, --version show version number and exit -d, --debug enable debug output - -I, --init initialize configuration directory in $HOME/.nr and exit + -I, --init [VERSION] initialize configuration directory in $HOME/.nr and exit. optionally, specify a VERSION to initialize with: -I 0.1.0 -c, --config CONFIG configuration file, default: $HOME/.nr/nrx.conf -i, --input INPUT input source: netbox (default) | cyjs -o, --output OUTPUT output format: cyjs | clab | cml | graphite | d2 or any other format supported by provided templates @@ -181,7 +188,7 @@ By default, **nrx** looks up for the following assets in the `$HOME/.nr` directo * Configuration file: `nrx.conf`, unless overridden by `--config` argument * Templates: `templates`, which can be supplemented by additional paths with `--templates` argument -To initialize the configuration directory, run `nrx.py --init`. This will create the `$HOME/.nr` folder and populate it with a configuration file example and a compatible version of the templates. +To initialize the configuration directory, run `nrx --init`. This will create the `$HOME/.nr` folder and populate it with a configuration file example and a compatible version of the templates. # Templates @@ -216,11 +223,11 @@ source nrx39/bin/activate ## Containerlab example -1. Run `./nrx.py --output clab` to export a topology graph from NetBox in Containerlab format. See [How to configure](#how-to-configure) for details. Here is an example of running `nrx.py` to export a graph for NetBox Site "DM-Albany" from [NetBox Demo](https://demo.netbox.dev) instance: +1. Run `nrx --output clab` to export a topology graph from NetBox in Containerlab format. See [How to configure](#how-to-configure) for details. Here is an example of running `nrx` to export a graph for NetBox Site "DM-Albany" from [NetBox Demo](https://demo.netbox.dev) instance: ```Shell export NB_API_TOKEN='replace_with_valid_API_token' - ./nrx.py --api https://demo.netbox.dev --templates templates --output clab --dir demo --site DM-Albany + nrx --api https://demo.netbox.dev --templates templates --output clab --dir demo --site DM-Albany ``` 2. Now you're ready to start the Containerlab topology. Here is the example for "DM-Albany" site @@ -229,26 +236,26 @@ source nrx39/bin/activate sudo -E containerlab deploy -t demo/DM-Albany.clab.yaml --reconfigure ``` -3. Without `--output clab` argument, `nrx.py` will save data from NetBox as a CYJS file `.cyjs` +3. Without `--output clab` argument, `nrx` will save data from NetBox as a CYJS file `.cyjs` ```Shell export NB_API_TOKEN='replace_with_valid_API_token' - ./nrx.py --api https://demo.netbox.dev --site DM-Albany --dir demo + nrx --api https://demo.netbox.dev --site DM-Albany --dir demo ``` -5. If you have a CYJS file, run `./nrx.py --input cyjs --file .cyjs --output clab` to create a Containerlab topology file from the CYJS graph you exported in the previous step. For example, run: +5. If you have a CYJS file, run `nrx --input cyjs --file .cyjs --output clab` to create a Containerlab topology file from the CYJS graph you exported in the previous step. For example, run: ```Shell - ./nrx.py --input cyjs --file demo/DM-Albany.cyjs --templates templates --output clab --dir demo + nrx --input cyjs --file demo/DM-Albany.cyjs --templates templates --output clab --dir demo ``` ## Cisco Modeling Labs example -1. Run `./nrx.py --output cml` to export a topology graph from NetBox in CML format. See [How to configure](#how-to-configure) for details. Here is an example of running `nrx.py` to export a graph for NetBox Site "DM-Akron" from [NetBox Demo](https://demo.netbox.dev) instance: +1. Run `nrx --output cml` to export a topology graph from NetBox in CML format. See [How to configure](#how-to-configure) for details. Here is an example of running `nrx` to export a graph for NetBox Site "DM-Akron" from [NetBox Demo](https://demo.netbox.dev) instance: ```Shell export NB_API_TOKEN='replace_with_valid_API_token' - ./nrx.py --api https://demo.netbox.dev --templates templates --output cml --dir demo --site DM-Akron + nrx --api https://demo.netbox.dev --templates templates --output cml --dir demo --site DM-Akron ``` 2. Now you're ready to start the "DM-Akron" topology in CML. @@ -259,17 +266,17 @@ source nrx39/bin/activate * Choose "GO TO LAB". In SIMULATE menu, choose START LAB * Use NODES menu to monitor the status of each node -3. Without `--output cml` argument, `nrx.py` will save data from NetBox as a CYJS file `.cyjs` +3. Without `--output cml` argument, `nrx` will save data from NetBox as a CYJS file `.cyjs` ```Shell export NB_API_TOKEN='replace_with_valid_API_token' - ./nrx.py --api https://demo.netbox.dev --dir demo --site DM-Akron + nrx --api https://demo.netbox.dev --dir demo --site DM-Akron ``` -4. If you have a CYJS file, run `./nrx.py --input cyjs --file .cyjs --output cml` to create a topology file from the CYJS graph you exported in the previous step. For example, run: +4. If you have a CYJS file, run `nrx --input cyjs --file .cyjs --output cml` to create a topology file from the CYJS graph you exported in the previous step. For example, run: ```Shell - ./nrx.py --input cyjs --file demo/DM-Akron.cyjs --templates templates --output cml --dir demo + nrx --input cyjs --file demo/DM-Akron.cyjs --templates templates --output cml --dir demo ``` ## Topology Visualization with Graphite @@ -278,11 +285,11 @@ A combination of **netreplica** `nrx` and [`graphite`](https://github.com/netrep Follow a two-step process: -1. Export topology data from NetBox in the Graphite format: `nrx.py -o graphite`. For example, let's export "DM-Akron" site from the [NetBox Demo](https://demo.netbox.dev) instance: +1. Export topology data from NetBox in the Graphite format: `nrx -o graphite`. For example, let's export "DM-Akron" site from the [NetBox Demo](https://demo.netbox.dev) instance: ```Shell export NB_API_TOKEN='replace_with_valid_API_token' - ./nrx.py --api https://demo.netbox.dev --site DM-Akron --templates templates --output graphite + nrx --api https://demo.netbox.dev --site DM-Akron --templates templates --output graphite ``` 2. Start Graphite to visualize "DM-Akron" site: @@ -341,7 +348,7 @@ Watch [the demo of the project on YouTube](https://youtu.be/cP8PUr306ZM): ## Copyright notice -Copyright 2023 Netreplica Team +Copyright 2023,2024 Netreplica Team Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/nrx b/nrx new file mode 100755 index 0000000..fb0cf41 --- /dev/null +++ b/nrx @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +# Copyright 2024 Netreplica Team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Export network topology from NetBox as a graph + +import re +import sys +import os + +# Get the directory containing the repository +repo_directory = os.path.dirname(os.path.abspath(__file__)) +# Append to the system path for module imports +sys.path.append(f"{repo_directory}/src") +# Import the main function and execute +from nrx import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) \ No newline at end of file diff --git a/nrx.py b/nrx.py deleted file mode 120000 index 23d8ad0..0000000 --- a/nrx.py +++ /dev/null @@ -1 +0,0 @@ -nrx/nrx.py \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0a6e49d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,47 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" +[project] +name = "nrx" +dynamic = [ + "version" +] +authors = [ + { name="Alex Bortok and Netreplica Team", email="dev@netreplica.com" }, +] +description = "nrx - netreplica exporter" +readme = "README.md" +requires-python = ">=3.9" +license = {file = "LICENSE"} +classifiers = [ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License" +] +dependencies = [ + "certifi", + "charset-normalizer>=3.3.2", + "idna>=3.6", + "Jinja2>=3.1.3", + "MarkupSafe>=2.1.4", + "networkx>=3.2.1", + "pynetbox>=7.3.3", + "requests>=2.31.0", + "toml>=0.10.2", + "urllib3>=2.1.0", + "pyyaml>=6.0.1" +] + +[project.urls] +Homepage = "https://github.com/netreplica/nrx" +Issues = "https://github.com/netreplica/nrx/issues" + +[project.scripts] +nrx = "nrx:main" + +[tool.hatch.build.targets.wheel] +sources = ["src"] +only-include = ["src/nrx"] + +[tool.hatch.version] +path = "src/nrx/__about__.py" \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt index 9476802..89e3240 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,2 +1,5 @@ pylint -rich \ No newline at end of file +rich +build +twine +hatch \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 7108262..b6ae427 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,4 @@ pynetbox==7.3.3 requests==2.31.0 toml==0.10.2 urllib3==2.1.0 -pyyaml==6.0.1 \ No newline at end of file +pyyaml==6.0.1 diff --git a/src/nrx/__about__.py b/src/nrx/__about__.py new file mode 100644 index 0000000..03a523b --- /dev/null +++ b/src/nrx/__about__.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +# nrx - network topology exporter by netreplica + +# Copyright 2024 Netreplica Team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Metadata for the nrx package +""" +__version__ = "0.5.0rc1" diff --git a/src/nrx/__init__.py b/src/nrx/__init__.py new file mode 100644 index 0000000..8e4b76e --- /dev/null +++ b/src/nrx/__init__.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +# nrx - network topology exporter by netreplica + +# Copyright 2024 Netreplica Team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Main function for nrx +""" + +from nrx.nrx import cli + +def main(): + """Main""" + return cli() diff --git a/nrx/nrx.py b/src/nrx/nrx.py similarity index 98% rename from nrx/nrx.py rename to src/nrx/nrx.py index 5224859..b9c8c62 100755 --- a/nrx/nrx.py +++ b/src/nrx/nrx.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2023 Netreplica Team +# Copyright 2023,2024 Netreplica Team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,9 +27,6 @@ It can also read the topology graph previously saved as a CYJS file to convert it into the one of supported network emulation formats. """ -__version__ = 'v0.4.0' -__author__ = 'Alex Bortok and Netreplica Team' - import os import sys import argparse @@ -47,6 +44,9 @@ import jinja2 import yaml +# Single source version +from nrx.__about__ import __version__ + # DEFINE GLOBAL VARs HERE DEBUG_ON = False @@ -989,7 +989,9 @@ def parse_args(): args_parser.add_argument('-v', '--version', action='version', version=f'%(prog)s {__version__}') args_parser.add_argument('-d', '--debug', nargs=0, action=NrxDebugAction, help='enable debug output') - args_parser.add_argument('-I', '--init', nargs=0, action=NrxInitAction, help=f"initialize configuration directory in $HOME/{NRX_CONFIG_DIR} and exit") + args_parser.add_argument('-I', '--init', nargs='?', help=f"initialize configuration directory in $HOME/{NRX_CONFIG_DIR} and exit. \ + optionally, specify a VERSION to initialize with: -I 0.1.0", + const=__version__, action=NrxInitAction, metavar='VERSION') args_parser.add_argument('-c', '--config', required=False, help=f"configuration file, default: $HOME/{NRX_CONFIG_DIR}/{NRX_DEFAULT_CONFIG_NAME}", default=nrx_default_config_path()) args_parser.add_argument('-i', '--input', required=False, help='input source: netbox (default) | cyjs', @@ -1032,11 +1034,12 @@ class NrxInitAction(argparse.Action): """Argparse action to initialize configuration directory""" def __call__(self, parser, namespace, values, option_string=None): # Create a NRX_CONFIG_DIR directory in the user's home directory, or in the current directory if HOME is not set + debug(f"[INIT] version to use: {values}") config_dir_path = nrx_config_dir() print(f"[INIT] Initializing configuration directory in {config_dir_path}") config_dir = create_dirs(config_dir_path) # Get asset NRX_VERSIONS_NAME with versions compatibility matrix - versions = get_versions(__version__) + versions = get_versions(values) templates_path = get_templates(versions, config_dir) if templates_path is not None: print(f"[INIT] Saved templates to: {templates_path}") @@ -1051,8 +1054,10 @@ def __call__(self, parser, namespace, values, option_string=None): def get_versions(nrx_version): - """Download and parse NRX_VERSIONS_NAME asset file for a specific nrx version""" - versions_url = f"{NRX_REPOSITORY}/releases/download/{nrx_version}/{NRX_VERSIONS_NAME}" + """ + Download and parse NRX_VERSIONS_NAME asset file for a matching nrx release version + """ + versions_url = f"{NRX_REPOSITORY}/releases/download/v{nrx_version}/{NRX_VERSIONS_NAME}" try: r = requests.get(versions_url, timeout=NRX_REPOSITORY_TIMEOUT) except (HTTPError, Timeout, RequestException) as e: @@ -1247,8 +1252,8 @@ def load_config(args): return config -def main(): - """Main""" +def cli(): + """Main entry for CLI execution, called from main() in __init__.py""" # Parameters args = parse_args() config = load_config(args) @@ -1289,7 +1294,3 @@ def main(): error(f"Only --input netbox is supported for this type of export format: {config['output_format']}") return 0 - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/versions.yaml b/versions.yaml index 238743e..f280643 100644 --- a/versions.yaml +++ b/versions.yaml @@ -1,2 +1,2 @@ -nrx: v0.4.0 +nrx: v0.5.0 templates: v0.2.0 \ No newline at end of file