diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index fb98ef7..97efb95 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -8,40 +8,35 @@ name: Python package -on: - push: - branches: [ "main", "dev" ] - # pull_request: - # branches: [ "main" ] +on: [push] jobs: build: - runs-on: ubuntu-latest strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - if [ -f tests/requirements.txt ]; then pip install -r tests/requirements.txt; fi - - name: Check code - run: | - make check - - name: Test with pytest - run: | - PYTHONPATH=src coverage run -m pytest - coverage lcov - - name: Code Coverage - uses: coverallsapp/github-action@master - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - path-to-lcov: coverage.lcov + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + if [ -f tests/requirements.txt ]; then pip install -r tests/requirements.txt; fi + - name: Check code + run: | + make check + - name: Test with pytest + run: | + PYTHONPATH=src coverage run -m pytest + coverage lcov + - name: Code Coverage + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + path-to-lcov: coverage.lcov diff --git a/README.md b/README.md index b3d35a2..0806dde 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,33 @@ Convert EVE-NG topology files either in ZIP format or in plain-text XML (.unl) t > > This is considered "beta" as in "it works for me but might have bugs or things don't work as you expect them to work." So, use with care and at your own risk. +### Online conversion + +The easiest way to convert files into CML format is to use the online converter provided [here](https://www.marcuscom.com/eve2cml/). Simply upload your UNL or ZIP files and the tool will convert them for you, using eve2cml package in the background. + +### Installation + +eve2cml requires Python 3, it has been tested with 3.9, 3.10, 3.11 and 3.12. You can either + +- use [pipx](https://github.com/pypa/pipx) to install it in an isolated environment: + ``` + $ pipx install eve2cml + installed package eve2cml 0.1.0b4, installed using Python 3.10.12 + These apps are now globally available + - eve2cml + done! ✨ 🌟 ✨ + + $ eve2cml --version + eve2cml 0.1.0b4 + + $ + ``` +- pip-install it from PyPI by running `pip install eve2cml`. This will install the package into your current Python environment. It is suggested to use a virtual or local environment instead of the system / global environment. + +### Development + +eve2cml uses [PDM](https://pdm-project.org/latest/) for dependency management. At a minimum, Git Python3 and PDM must be installed as prerequisites. After PDM is installed, you can run `pdm install` and a virtual environment will be created, all dependencies will be installed and a dev-version of eve2cml will be available. Changes should be pushed into a new branch to a forked copy of the repository and result, eventually, in a pull request. + ### Mapping node types There's some default node type mappings which can be dumped into a file using the `--dump` flag. @@ -117,7 +144,7 @@ If you encounter any issues with the converter then please open a issue in the [ There's a known issue with the maximum number of interfaces defined in the shipping node definitions for IOL and IOL-L2. As for the released version of CML 2.7.0, the maximum number of interfaces is 16. If you have topologies which use more interfaces (`Ethernet4/0` and up) then you need to add the additional interfaces to the node definition in CML (Tools -> Node and image definitions -> IOL / IOL-L2). In the interface section, add the required interfaces and name them properly. Don't forget to click "Update" at the bottom when done. -This is likely fixed in the 2.7.1 release, once available. +This is fixed in the 2.7.1 release, once available. The converter has now 32 interfaces defined in the mapper (`Ethernet0/0` to `Ethernet7/3`). @@ -125,7 +152,7 @@ The converter has now 32 interfaces defined in the mapper (`Ethernet0/0` to `Eth There's a few things which are known to cause issues. Some of them might be addressable by code changes in the converter and/or changes on the CML side of things. And some might just not be possible at all. -- Rotation of annotation only works for text. Ellipses and rectangles don't support rotation on the CML side of things. To be addressed in a future CML release +- Rotation of annotation only works for text. Ellipses and rectangles don't support rotation on the CML side of things. To be addressed in a future CML release. - Text objects in EVE can have a background color. The converter adds additional rectangles behind the text object in CML. It "guesses" the size of these rectangles. Those guesses are inaccurate. - Font (names) might not translate well. I think there's a general issue with serif vs. sans-serif mapping. - Images (PNGs) as part of a topology are ignored as there's no representation for them in CML. diff --git a/pdm.lock b/pdm.lock index 4900f22..4a1442b 100644 --- a/pdm.lock +++ b/pdm.lock @@ -4,8 +4,8 @@ [metadata] groups = ["default", "dev"] strategy = ["cross_platform", "inherit_metadata"] -lock_version = "4.4.1" -content_hash = "sha256:12877642b3b25031f2ee3fe7b681bbb2d71f4723e42a233e654acac770a4fd1b" +lock_version = "4.4.2" +content_hash = "sha256:f772ab6773f7042f88a1e5a136aa4df998e10dc3e4f4a139b6a049a6e76f4eb9" [[package]] name = "beautifulsoup4" @@ -185,7 +185,7 @@ files = [ [[package]] name = "mypy" -version = "1.9.0" +version = "1.10.1" requires_python = ">=3.8" summary = "Optional static typing for Python" groups = ["dev"] @@ -195,33 +195,33 @@ dependencies = [ "typing-extensions>=4.1.0", ] files = [ - {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, - {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, - {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, - {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, - {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, - {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, - {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, - {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, - {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, - {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, - {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, - {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, - {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, - {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, - {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, - {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, - {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, - {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, - {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, + {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"}, + {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"}, + {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"}, + {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"}, + {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"}, + {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"}, + {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"}, + {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"}, + {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"}, + {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"}, + {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"}, + {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"}, + {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"}, + {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"}, + {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"}, + {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"}, + {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"}, + {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"}, + {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"}, ] [[package]] @@ -248,18 +248,18 @@ files = [ [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" requires_python = ">=3.8" summary = "plugin and hook calling mechanisms for python" groups = ["dev"] files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [[package]] name = "pytest" -version = "8.1.1" +version = "8.2.2" requires_python = ">=3.8" summary = "pytest: simple powerful testing with Python" groups = ["dev"] @@ -268,12 +268,12 @@ dependencies = [ "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", "iniconfig", "packaging", - "pluggy<2.0,>=1.4", + "pluggy<2.0,>=1.5", "tomli>=1; python_version < \"3.11\"", ] files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, ] [[package]] @@ -341,28 +341,29 @@ files = [ [[package]] name = "ruff" -version = "0.4.1" +version = "0.5.2" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["dev"] files = [ - {file = "ruff-0.4.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2d9ef6231e3fbdc0b8c72404a1a0c46fd0dcea84efca83beb4681c318ea6a953"}, - {file = "ruff-0.4.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9485f54a7189e6f7433e0058cf8581bee45c31a25cd69009d2a040d1bd4bfaef"}, - {file = "ruff-0.4.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2921ac03ce1383e360e8a95442ffb0d757a6a7ddd9a5be68561a671e0e5807e"}, - {file = "ruff-0.4.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eec8d185fe193ad053eda3a6be23069e0c8ba8c5d20bc5ace6e3b9e37d246d3f"}, - {file = "ruff-0.4.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa27d9d72a94574d250f42b7640b3bd2edc4c58ac8ac2778a8c82374bb27984"}, - {file = "ruff-0.4.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f1ee41580bff1a651339eb3337c20c12f4037f6110a36ae4a2d864c52e5ef954"}, - {file = "ruff-0.4.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0926cefb57fc5fced629603fbd1a23d458b25418681d96823992ba975f050c2b"}, - {file = "ruff-0.4.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c6e37f2e3cd74496a74af9a4fa67b547ab3ca137688c484749189bf3a686ceb"}, - {file = "ruff-0.4.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd703a5975ac1998c2cc5e9494e13b28f31e66c616b0a76e206de2562e0843c"}, - {file = "ruff-0.4.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b92f03b4aa9fa23e1799b40f15f8b95cdc418782a567d6c43def65e1bbb7f1cf"}, - {file = "ruff-0.4.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1c859f294f8633889e7d77de228b203eb0e9a03071b72b5989d89a0cf98ee262"}, - {file = "ruff-0.4.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b34510141e393519a47f2d7b8216fec747ea1f2c81e85f076e9f2910588d4b64"}, - {file = "ruff-0.4.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6e68d248ed688b9d69fd4d18737edcbb79c98b251bba5a2b031ce2470224bdf9"}, - {file = "ruff-0.4.1-py3-none-win32.whl", hash = "sha256:b90506f3d6d1f41f43f9b7b5ff845aeefabed6d2494307bc7b178360a8805252"}, - {file = "ruff-0.4.1-py3-none-win_amd64.whl", hash = "sha256:c7d391e5936af5c9e252743d767c564670dc3889aff460d35c518ee76e4b26d7"}, - {file = "ruff-0.4.1-py3-none-win_arm64.whl", hash = "sha256:a1eaf03d87e6a7cd5e661d36d8c6e874693cb9bc3049d110bc9a97b350680c43"}, - {file = "ruff-0.4.1.tar.gz", hash = "sha256:d592116cdbb65f8b1b7e2a2b48297eb865f6bdc20641879aa9d7b9c11d86db79"}, + {file = "ruff-0.5.2-py3-none-linux_armv6l.whl", hash = "sha256:7bab8345df60f9368d5f4594bfb8b71157496b44c30ff035d1d01972e764d3be"}, + {file = "ruff-0.5.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:1aa7acad382ada0189dbe76095cf0a36cd0036779607c397ffdea16517f535b1"}, + {file = "ruff-0.5.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:aec618d5a0cdba5592c60c2dee7d9c865180627f1a4a691257dea14ac1aa264d"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b62adc5ce81780ff04077e88bac0986363e4a3260ad3ef11ae9c14aa0e67ef"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dc42ebf56ede83cb080a50eba35a06e636775649a1ffd03dc986533f878702a3"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c15c6e9f88c67ffa442681365d11df38afb11059fc44238e71a9d9f1fd51de70"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d3de9a5960f72c335ef00763d861fc5005ef0644cb260ba1b5a115a102157251"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe5a968ae933e8f7627a7b2fc8893336ac2be0eb0aace762d3421f6e8f7b7f83"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a04f54a9018f75615ae52f36ea1c5515e356e5d5e214b22609ddb546baef7132"}, + {file = "ruff-0.5.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed02fb52e3741f0738db5f93e10ae0fb5c71eb33a4f2ba87c9a2fa97462a649"}, + {file = "ruff-0.5.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3cf8fe659f6362530435d97d738eb413e9f090e7e993f88711b0377fbdc99f60"}, + {file = "ruff-0.5.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:237a37e673e9f3cbfff0d2243e797c4862a44c93d2f52a52021c1a1b0899f846"}, + {file = "ruff-0.5.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2a2949ce7c1cbd8317432ada80fe32156df825b2fd611688814c8557824ef060"}, + {file = "ruff-0.5.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:481af57c8e99da92ad168924fd82220266043c8255942a1cb87958b108ac9335"}, + {file = "ruff-0.5.2-py3-none-win32.whl", hash = "sha256:f1aea290c56d913e363066d83d3fc26848814a1fed3d72144ff9c930e8c7c718"}, + {file = "ruff-0.5.2-py3-none-win_amd64.whl", hash = "sha256:8532660b72b5d94d2a0a7a27ae7b9b40053662d00357bb2a6864dd7e38819084"}, + {file = "ruff-0.5.2-py3-none-win_arm64.whl", hash = "sha256:73439805c5cb68f364d826a5c5c4b6c798ded6b7ebaa4011f01ce6c94e4d5583"}, + {file = "ruff-0.5.2.tar.gz", hash = "sha256:2c0df2d2de685433794a14d8d2e240df619b748fbe3367346baa519d8e6f1ca2"}, ] [[package]] @@ -390,7 +391,7 @@ files = [ [[package]] name = "types-beautifulsoup4" -version = "4.12.0.20240229" +version = "4.12.0.20240511" requires_python = ">=3.8" summary = "Typing stubs for beautifulsoup4" groups = ["dev"] @@ -398,8 +399,8 @@ dependencies = [ "types-html5lib", ] files = [ - {file = "types-beautifulsoup4-4.12.0.20240229.tar.gz", hash = "sha256:e37e4cfa11b03b01775732e56d2c010cb24ee107786277bae6bc0fa3e305b686"}, - {file = "types_beautifulsoup4-4.12.0.20240229-py3-none-any.whl", hash = "sha256:000cdddb8aee4effb45a04be95654de8629fb8594a4f2f1231cff81108977324"}, + {file = "types-beautifulsoup4-4.12.0.20240511.tar.gz", hash = "sha256:004f6096fdd83b19cdbf6cb10e4eae57b10205eccc365d0a69d77da836012e28"}, + {file = "types_beautifulsoup4-4.12.0.20240511-py3-none-any.whl", hash = "sha256:7ceda66a93ba28d759d5046d7fec9f4cad2f563a77b3a789efc90bcadafeefd1"}, ] [[package]] diff --git a/src/eve2cml/_version.py b/src/eve2cml/_version.py index bb43089..c26649b 100644 --- a/src/eve2cml/_version.py +++ b/src/eve2cml/_version.py @@ -1 +1 @@ -__version__ = "0.1.0b4" +__version__ = "0.1.0b5" diff --git a/src/eve2cml/main.py b/src/eve2cml/main.py index e394170..6edcaeb 100644 --- a/src/eve2cml/main.py +++ b/src/eve2cml/main.py @@ -130,6 +130,18 @@ def dump_as_text(out: io.TextIOWrapper, lab: Lab, dump_all: bool): out.write("\n") +def centered_line_with_stars(name="", cols=80) -> str: + if len(name) == 0: + return "*" * cols + num_asterisks = cols - len(name) + 2 + asterisks_each_side = num_asterisks // 2 + asterisks_left = "*" * asterisks_each_side + asterisks_right = "*" * ( + asterisks_each_side if num_asterisks % 2 == 0 else asterisks_each_side + 1 + ) + return f"{asterisks_left} {name} {asterisks_right}" + + def main(): parser = argparse.ArgumentParser( description="Convert UNL/XML topologies to CML2 topologies" @@ -190,12 +202,20 @@ def yaml_multiline_string_pipe(dumper, data): return dumper.represent_scalar("tag:yaml.org,2002:str", fixed_data) yaml.add_representer(str, yaml_multiline_string_pipe) + + if args.stdout: + for lab in labs: + print( + centered_line_with_stars( + str(Path(lab.filename).with_suffix(".yaml")) + ) + ) + sys.stdout.write(yaml.dump(lab.as_cml_dict(), sort_keys=False)) + print(centered_line_with_stars()) + return + for lab in labs: - cml_filename = ( - sys.stdout.fileno() - if args.stdout - else Path(lab.filename).with_suffix(".yaml") - ) + cml_filename = Path(lab.filename).with_suffix(".yaml") with open(cml_filename, "w", encoding="utf-8") as cml_file: cml_file.write(yaml.dump(lab.as_cml_dict(), sort_keys=False)) return diff --git a/src/eve2cml/mapper.py b/src/eve2cml/mapper.py index 6259810..50bb6ad 100644 --- a/src/eve2cml/mapper.py +++ b/src/eve2cml/mapper.py @@ -1,13 +1,12 @@ import io import logging import sys -from importlib import resources +from importlib.resources import files from pathlib import Path from typing import Dict, List, Optional import yaml - -from . import map_data as md +from yaml.parser import ParserError _LOGGER = logging.getLogger(__name__) @@ -49,7 +48,9 @@ def dump(self, out: io.TextIOWrapper): @classmethod def load(cls, filename="") -> "Eve2CMLmapper": - map_data = yaml.safe_load(resources.read_text(md, "default.yaml")) + map_data = yaml.safe_load( + files("eve2cml.map_data").joinpath("default.yaml").read_text() + ) if filename: map_file = Path(filename) @@ -57,9 +58,12 @@ def load(cls, filename="") -> "Eve2CMLmapper": with open(map_file) as fh: try: map_data = yaml.safe_load(fh) - except Exception as exc: + except ParserError as exc: _LOGGER.critical("can't decode %s: %s", filename, exc) sys.exit(1) + if not isinstance(map_data, dict): + _LOGGER.critical("can't use provided mapper file") + sys.exit(1) _LOGGER.warning("custom mapper loaded: %s", filename) else: _LOGGER.error("mapper provided but not found. Using built-in mapper!") @@ -73,7 +77,9 @@ def load(cls, filename="") -> "Eve2CMLmapper": return mapper def node_def(self, obj_type: str, template: str, image: str) -> CMLdef: - lookup = f"{obj_type}:{template}:{image}" + lookup = f"{obj_type}:{template}" + if len(image) > 0: + lookup += f"{lookup}:{image}" found = self.map.get(lookup) if not found: # special case for non-template images like IOL or Docker diff --git a/tests/requirements.txt b/tests/requirements.txt index 087de46..9e7a6c3 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -3,20 +3,20 @@ beautifulsoup4==4.12.3 colorama==0.4.6; sys_platform == "win32" -coverage==7.4.3 +coverage[toml]==7.4.3 exceptiongroup==1.2.0; python_version < "3.11" iniconfig==2.0.0 -mypy==1.9.0 +mypy==1.10.1 mypy-extensions==1.0.0 packaging==23.2 -pluggy==1.4.0 -pytest==8.1.1 +pluggy==1.5.0 +pytest==8.2.2 pytest-cov==5.0.0 pyyaml==6.0.1 -ruff==0.4.1 +ruff==0.5.2 soupsieve==2.5 tomli==2.0.1; python_version < "3.11" -types-beautifulsoup4==4.12.0.20240229 +types-beautifulsoup4==4.12.0.20240511 types-html5lib==1.1.11.20240228 types-pyyaml==6.0.12.20240311 typing-extensions==4.10.0 diff --git a/tests/test_centered_line.py b/tests/test_centered_line.py new file mode 100644 index 0000000..8ee505d --- /dev/null +++ b/tests/test_centered_line.py @@ -0,0 +1,11 @@ +import eve2cml.main + + +def test_centered_line(): + # Test when data is None + full = eve2cml.main.centered_line_with_stars() + assert len(full) == 80 + + partial = eve2cml.main.centered_line_with_stars("this") + assert len(full) == 80 + assert " this " in partial diff --git a/tests/test_mapper.py b/tests/test_mapper.py new file mode 100644 index 0000000..f34f5cc --- /dev/null +++ b/tests/test_mapper.py @@ -0,0 +1,57 @@ +import logging +from pathlib import Path + +import pytest +from eve2cml import mapper + + +def test_mapper(): + m = mapper.Eve2CMLmapper().load() + assert m + + d = m.as_dict() + assert d + + with open("/dev/null", "w", encoding="utf-8") as out: + m.dump(out) + + nd = m.node_def("iol", "iol", "i86bi_linux_l2") + assert nd + + nd = m.node_def("qemu", "vios", "") + assert nd + + label = m.cml_iface_label(0, "iosv", "gaga") + assert label != "gaga" + + label = m.cml_iface_label(0, "doesntexist", "gaga") + assert label == "gaga" + + +def test_custom_mapper(request, caplog): + caplog.set_level(logging.INFO) + + caplog.clear() + _ = mapper.Eve2CMLmapper().load("doesntexist") + assert "mapper provided but not found" in caplog.text + + caplog.clear() + testdata = Path(request.path).parent / "testdata" / "map1.yaml" + _ = mapper.Eve2CMLmapper().load(str(testdata)) + assert "custom mapper loaded" in caplog.text + + caplog.clear() + testdata = Path(request.path).parent / "testdata" / "map2.yaml" + with pytest.raises(SystemExit) as pytest_wrapped_e: + _ = mapper.Eve2CMLmapper().load(str(testdata)) + assert pytest_wrapped_e.type is SystemExit + assert pytest_wrapped_e.value.code == 1 + assert "can't decode" in caplog.text + + caplog.clear() + testdata = Path(request.path).parent / "testdata" / "hub.unl" + with pytest.raises(SystemExit) as pytest_wrapped_e: + _ = mapper.Eve2CMLmapper().load(str(testdata)) + assert pytest_wrapped_e.type is SystemExit + assert pytest_wrapped_e.value.code == 1 + assert "can't use provided mapper" in caplog.text diff --git a/tests/testdata/map1.yaml b/tests/testdata/map1.yaml new file mode 100644 index 0000000..6543561 --- /dev/null +++ b/tests/testdata/map1.yaml @@ -0,0 +1,14 @@ +interface_lists: + desktop: + - eth0 + - eth1 + - eth2 + - eth3 + external_connector: + - port +map: + docker:docker: + image_def: null + node_def: desktop + override: true +unknown_type: desktop diff --git a/tests/testdata/map2.yaml b/tests/testdata/map2.yaml new file mode 100644 index 0000000..bca40f0 --- /dev/null +++ b/tests/testdata/map2.yaml @@ -0,0 +1,14 @@ +interface_lists: + desktop: + - {eth0 + - eth1 + - eth2 + - eth3 + external_connector: + - port +map: + docker:docker: + image_def: null + node_def: desktop + override: true +unknown_type: desktop