diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..7530c79 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,15 @@ +name: CI + +on: + push: + branches: [main] + tags: ["v*"] + pull_request: + +jobs: + driver: + uses: NAPALM/driver-standard/.gitea/workflows/driver.yml@main + with: + standard-ref: main + secrets: + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index a7bc4d4..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: CI - -on: - push: - branches: ["**"] - pull_request: - branches: ["**"] - -jobs: - test: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ["3.10", "3.11", "3.12"] - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: pip - - - name: Install package with dev extras - run: | - python -m pip install --upgrade pip - # napalm-device-types lives in git.netork.io/NAPALM, not on PyPI: without this - # pip looks there, finds an unrelated 0.1.0 and the job dies before any test. - python -m pip install "napalm-device-types @ git+https://git.netork.io/NAPALM/napalm-device-types.git" - python -m pip install -e ".[dev]" - - - name: Run unit tests - run: | - python -m pytest -q --tb=short - - - name: Build wheel and sdist - run: | - python -m pip install build - python -m build - - - name: Upload dist artifacts - # v4 refuses to run on Gitea ("not currently supported on GHES"). - uses: actions/upload-artifact@v3 - with: - name: dist-${{ matrix.python-version }} - path: dist/* \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 31af1eb..8bc0390 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- The driver follows the NAPALM driver standard + (https://git.netork.io/NAPALM/driver-standard): CI on Gitea runs ruff, mypy, the + tests on Python 3.10–3.13 and at the lowest declared versions, bandit, pip-audit and + a build check, and a release tag publishes to PyPI. Python 3.10 remains the floor; + requires napalm-device-types 4.21.1. +- Provisioning fails, and removes the imported VM, when vSphere hands back fewer + network adapters than NICs were asked for, instead of leaving a NIC unconfigured. + A failed abort of the import lease is logged at debug level. + ### Added - VM provisioning (`create_vm_from_cloud_init`, `destroy_vm`, `get_vm_status`, `get_network_targets`, `get_image_storages`) from qcow2/raw diff --git a/README.md b/README.md index 19cde89..3db982d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # napalm-vmware NAPALM drivers for VMware vSphere, speaking the vSphere API through -[pyVmomi](https://github.com/vmware/pyvmomi): +[pyVmomi](https://pypi.org/project/pyvmomi/): | Driver | Endpoint | Device in netOrk | |---|---|---| @@ -57,8 +57,10 @@ Unverified assumptions, to be checked against real hardware: ## Install +Python 3.10 or later. + ```bash -pip install -e vendor/napalm-device-types/ -e vendor/napalm-vmware/ +pip install napalm-vmware ``` ## Usage @@ -86,8 +88,11 @@ by two VMs is refused rather than guessed. ## Tests +The rules are in [STANDARDS.md](https://git.netork.io/NAPALM/driver-standard/src/branch/main/STANDARDS.md). + ```bash -pytest # unit tests, no network +python -m venv .venv && .venv/bin/pip install -e ".[dev]" +.venv/bin/pytest --cov # unit tests, no network docker run -d --rm -p 127.0.0.1:8989:8989 vmware/vcsim -l 0.0.0.0:8989 docker run -d --rm -p 127.0.0.1:8990:8989 vmware/vcsim -esx -l 0.0.0.0:8989 VCSIM_VCENTER_PORT=8989 VCSIM_ESXI_PORT=8990 pytest -m vcsim diff --git a/pyproject.toml b/pyproject.toml index 18ff110..982e0d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=68", "wheel"] +requires = ["setuptools>=77"] build-backend = "setuptools.build_meta" [project] @@ -8,7 +8,7 @@ version = "0.2.0" description = "NAPALM drivers for VMware ESXi hosts and vCenter Server (vSphere API via pyVmomi)" readme = "README.md" requires-python = ">=3.10" -license = { text = "Apache-2.0" } +license = "Apache-2.0" authors = [ { name = "Christian Manivong", email = "christian@manivong.de" }, ] @@ -17,19 +17,19 @@ classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Intended Audience :: System Administrators", - "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: System :: Networking", "Topic :: System :: Systems Administration", "Typing :: Typed", ] dependencies = [ "napalm>=5.0.0", - "napalm-device-types>=4.14.0", + "napalm-device-types>=4.21.1", "pyvmomi>=8.0.1", # Provisioning: download the image, write the NoCloud seed ISO and its YAML. "requests>=2.31", @@ -39,10 +39,10 @@ dependencies = [ [project.optional-dependencies] dev = [ - "pytest", - "pytest-cov", - "ruff", - "mypy", + "pytest>=8", + "pytest-cov>=5", + "mypy>=1.11", + "ruff>=0.6", ] [project.entry-points."napalm.drivers"] @@ -51,6 +51,8 @@ vmware_vcenter = "napalm_vmware:VmwareVcenterDriver" [project.urls] Repository = "https://git.netork.io/NAPALM/napalm-vmware" +Issues = "https://git.netork.io/NAPALM/napalm-vmware/issues" +Changelog = "https://git.netork.io/NAPALM/napalm-vmware/src/branch/main/CHANGELOG.md" [tool.setuptools.packages.find] where = ["."] @@ -78,5 +80,33 @@ select = ["E", "F", "I", "UP", "B"] [tool.mypy] python_version = "3.10" -warn_unused_configs = true +strict = true +# napalm itself is untyped, so every driver subclasses Any. +disallow_subclassing_any = false + +[[tool.mypy.overrides]] +module = ["napalm.*", "yaml.*"] ignore_missing_imports = true + +[[tool.mypy.overrides]] +# Not yet under strict mypy (quality baseline); may only shrink. +module = [ + "napalm_vmware._inventory", + "napalm_vmware._session", + "napalm_vmware._tasks", + "napalm_vmware.base", + "napalm_vmware.esxi", + "napalm_vmware.parse.environment", + "napalm_vmware.parse.snapshots", + "napalm_vmware.parse.vm_devices", + "napalm_vmware.provision.seed", + "napalm_vmware.provision.transfer", + "napalm_vmware.provisioning", +] +ignore_errors = true + +[tool.coverage.run] +source = ["napalm_vmware"] + +[tool.coverage.report] +fail_under = 97 diff --git a/tests/quality_baseline.json b/tests/quality_baseline.json new file mode 100644 index 0000000..ff722b6 --- /dev/null +++ b/tests/quality_baseline.json @@ -0,0 +1,31 @@ +{ + "coverage_floor": 97.0, + "function_complexity": { + "napalm_vmware/_plain.py::to_plain": 11, + "napalm_vmware/base.py::VmwareBaseDriver._find_vm": 12, + "napalm_vmware/parse/facts.py::host_serial": 11, + "napalm_vmware/parse/interfaces.py::host_interfaces": 11, + "napalm_vmware/parse/vms.py::_vm": 13, + "napalm_vmware/provision/placement.py::choose_placement": 16 + }, + "function_lines": { + "napalm_vmware/provision/ovf.py::ovf_descriptor": 80, + "napalm_vmware/provisioning.py::VmwareProvisioningMixin._import_ovf": 56, + "napalm_vmware/provisioning.py::VmwareProvisioningMixin.create_vm_from_cloud_init": 57 + }, + "mypy_relaxed": [ + "napalm_vmware._inventory", + "napalm_vmware._session", + "napalm_vmware._tasks", + "napalm_vmware.base", + "napalm_vmware.esxi", + "napalm_vmware.parse.environment", + "napalm_vmware.parse.snapshots", + "napalm_vmware.parse.vm_devices", + "napalm_vmware.provision.seed", + "napalm_vmware.provision.transfer", + "napalm_vmware.provisioning" + ], + "python_file_lines": {}, + "untyped_public": [] +}