Skip to content

Commit 22693cd

Browse files
authored
Release/v2.8.0 (#124)
* SIMPLE-7063 Added group_display_attribute and refactored test_auth (#122) Do not fail if importing topologies for previous CML versions * Removed unexpected behavior of import lab method (#120) When a lab with a specific title existed on a CML instance, we joined that lab instead of creating a new lab with the same title which would had been perfectly fine. I believe that this was some obsolete feature request from pre-CML 2.0 era where labs possibly were enforced to have unique titles. * SIMPLE-6969 Changed PUT to PATCH for /system/auth/config * SIMPLE-6964 Dropped smart annotation tag and font properties (#119) * SIMPLE-6883 Support for smart annotations (#118) * SIMPLE-6813 Added deployed_mac_address property to Interface (#116) * SIMPLE-6776 local groups should still work with unset group filter (#115) * SIMPLE-6827 - removed MAC address block support (#114) * Dropped support for python3.8 in github actions * SIMPLE-6834 integration tests for ldap groups (#113) * Added the virlutils option to disable SSL check via ENV (#111) * SIMPLE-6706 fixed the 'owner' attribute of a joined lab (#108) * SIMPLE-6764 ldap group tests and system auth test (#110) * SIMPLE-6615 custom mac addresses implementation (#106) * SIMPLE-6397 Added support for 2.8, updated obsolete controller versions (#102) * SIMPLE-6345 refactored API error handling for better tracebacks (#98) * SIMPLE-6506 removed/deprecated offline mode leftovers (#97) * SIMPLE-6429 fix potentially mishandled data structures (#94) * SIMPLE-6384 Added rotation attribute to ellipse and rectangle annotations (#96) * Re-added api/* to the doc source tree removed by mistake * SIMPLE-6410 SQ issues is PCL (#90)
1 parent 2b9e98f commit 22693cd

31 files changed

+2749
-1533
lines changed

.github/workflows/main.yml

+10-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ jobs:
2121
runs-on: ubuntu-latest
2222
strategy:
2323
matrix:
24-
python-version: [3.8, 3.9]
24+
python-version:
25+
- "3.8"
26+
- "3.9"
27+
- "3.10"
28+
- "3.11"
29+
- "3.12"
2530

2631
steps:
2732
- uses: actions/checkout@v2
@@ -30,15 +35,18 @@ jobs:
3035
with:
3136
python-version: ${{ matrix.python-version }}
3237
- name: Install dependencies
38+
if: matrix.python-version != '3.8'
3339
run: |
3440
python -m pip install --upgrade pip
35-
if [ -f tests/requirements.txt ]; then pip install -r tests/requirements.txt; fi
41+
pip install -r tests/requirements.txt
3642
- name: Lint with flake8
43+
if: matrix.python-version != '3.8'
3744
run: |
3845
# stop the build if there are Python syntax errors or undefined names
3946
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
4047
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
4148
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
4249
- name: Test with pytest
50+
if: matrix.python-version != '3.8'
4351
run: |
4452
pytest

.pre-commit-config.yaml

+9-3
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,24 @@
44

55
repos:
66
- repo: https://github.com/pre-commit/pre-commit-hooks
7-
rev: v4.4.0
7+
rev: v4.6.0
88
hooks:
99
- id: trailing-whitespace
1010
- id: end-of-file-fixer
1111
- id: check-added-large-files
12+
- repo: https://github.com/astral-sh/ruff-pre-commit
13+
rev: v0.6.3
14+
hooks:
15+
- id: ruff
16+
args: [ --fix ]
17+
- id: ruff-format
1218
- repo: https://github.com/PyCQA/isort
13-
rev: 5.12.0
19+
rev: 5.13.2
1420
hooks:
1521
- id: isort
1622
args: [--profile, black]
1723
- repo: https://github.com/psf/black
18-
rev: 23.3.0
24+
rev: 24.8.0
1925
hooks:
2026
- id: black
2127
language_version: python3

docs/source/conf.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
sys.path.insert(0, os.path.abspath("../virl2_client"))
2323

24+
VIRL2_DOC = "virl2_client Documentation"
2425

2526
# -- Project information -----------------------------------------------------
2627

@@ -139,7 +140,7 @@
139140
(
140141
master_doc,
141142
"virl2_client.tex",
142-
"virl2_client Documentation",
143+
VIRL2_DOC,
143144
"VIRL2 team <[email protected]>",
144145
"manual",
145146
),
@@ -150,7 +151,7 @@
150151

151152
# One entry per manual page. List of tuples
152153
# (source start file, name, description, authors, manual section).
153-
man_pages = [(master_doc, "virl2_client", "virl2_client Documentation", [author], 1)]
154+
man_pages = [(master_doc, "virl2_client", VIRL2_DOC, [author], 1)]
154155

155156

156157
# -- Options for Texinfo output ----------------------------------------------
@@ -162,7 +163,7 @@
162163
(
163164
master_doc,
164165
"virl2_client",
165-
"virl2_client Documentation",
166+
VIRL2_DOC,
166167
author,
167168
"virl2_client",
168169
"One line description of project.",

docs/source/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ CML 2 controller.
1414

1515
intro
1616
examples
17+
api/*
1718

1819
.. only:: internal
1920

poetry.lock

+1,263-1,096
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "virl2_client"
3-
version = "2.7.1"
3+
version = "2.8.0"
44
description = "VIRL2 Client Library"
55
authors = ["Simon Knight <[email protected]>", "Ralph Schmieder <[email protected]>"]
66
license = "Apache-2.0"
@@ -15,13 +15,13 @@ classifiers = [
1515
"Environment :: Console",
1616
"License :: OSI Approved :: Apache Software License",
1717
"Operating System :: OS Independent",
18-
"Programming Language :: Python :: 3.8",
18+
"Programming Language :: Python :: 3.12",
1919
"Topic :: System :: Networking",
2020
]
2121

2222
[tool.poetry.dependencies]
23-
python = "^3.8.1"
24-
httpx = "^0.26.0"
23+
python = "^3.9.0"
24+
httpx = "^0.27.0"
2525

2626
# https://github.com/python-poetry/poetry/pull/606 -- no support for optional dev-deps
2727
# optional package for events
@@ -33,15 +33,15 @@ aiohttp = {version = "^3.8", optional = true}
3333
pyats = {version = "^24", optional = true}
3434

3535
# optional packages for documentation build
36-
sphinx_rtd_theme = {version="^1", optional = true}
36+
sphinx_rtd_theme = {version="^2", optional = true}
37+
# version 8 is available but requires python>=3.10
3738
sphinx = {version="^7", optional = true}
3839

3940
[tool.poetry.group.dev.dependencies]
4041
flake8 = "^7"
4142
pre-commit = "^3"
42-
# https://github.com/pytest-dev/pytest-asyncio/issues/737
43-
pytest = "<8"
44-
respx = "^0.20.0"
43+
pytest = "*"
44+
respx = "^0.21.0"
4545

4646
[tool.poetry.extras]
4747
events = ["aiohttp"]

pytest.ini

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ addopts =
1212
junit_family=xunit1
1313

1414
asyncio_mode=auto
15+
asyncio_default_fixture_loop_scope=function
1516

1617
# start pytest with --log-level=info
1718
# log_cli=True

tests/conftest.py

+4-7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import httpx
2525
import pytest
2626

27+
from virl2_client.models import authentication
2728
from virl2_client.virl2_client import ClientLibrary
2829

2930
CURRENT_VERSION = ClientLibrary.VERSION.version_str
@@ -48,11 +49,6 @@ def client_library_server_2_0_0():
4849
yield from client_library_patched_system_info(version="2.0.0")
4950

5051

51-
@pytest.fixture
52-
def client_library_server_1_0_0():
53-
yield from client_library_patched_system_info(version="1.0.0")
54-
55-
5652
@pytest.fixture
5753
def client_library_server_2_9_0():
5854
yield from client_library_patched_system_info(version="2.9.0")
@@ -65,7 +61,7 @@ def client_library_server_2_19_0():
6561

6662
@pytest.fixture
6763
def mocked_session():
68-
with patch.object(httpx, "Client", autospec=True) as session:
64+
with patch.object(authentication, "CustomClient", autospec=True) as session:
6965
yield session
7066

7167

@@ -97,13 +93,14 @@ def respx_mock_with_labs(respx_mock):
9793
some runtime data, like node states and simulation_statistics.
9894
"""
9995
respx_mock.get(FAKE_HOST_API + "system_information").respond(
100-
json={"version": CURRENT_VERSION, "ready": True},
96+
json={"version": CURRENT_VERSION, "ready": True, "oui": "52:54:00:00:00:00"},
10197
)
10298
respx_mock.post(FAKE_HOST_API + "authenticate").respond(json="BOGUS_TOKEN")
10399
respx_mock.get(FAKE_HOST_API + "authok")
104100
respx_mock.get(
105101
FAKE_HOST_API + "labs/444a78d1-575c-4746-8469-696e580f17b6/resource_pools"
106102
).respond(json=[])
103+
respx_mock.get(FAKE_HOST_API + "users").respond(json=[])
107104
respx_mock.get(FAKE_HOST_API + "resource_pools?data=true").respond(json=[])
108105
nodes = [
109106
"99cda47a-ecb2-4d31-86c4-74e7a8201958",

tests/requirements.txt

+29-30
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,29 @@
1-
anyio==4.3.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
2-
certifi==2024.2.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
3-
cfgv==3.4.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
4-
colorama==0.4.6 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" and sys_platform == "win32"
5-
distlib==0.3.8 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
6-
exceptiongroup==1.2.1 ; python_full_version >= "3.8.1" and python_version < "3.11"
7-
filelock==3.14.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
8-
flake8==7.0.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
9-
h11==0.14.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
10-
httpcore==1.0.5 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
11-
httpx==0.26.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
12-
identify==2.5.36 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
13-
idna==3.7 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
14-
iniconfig==2.0.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
15-
mccabe==0.7.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
16-
nodeenv==1.8.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
17-
packaging==24.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
18-
platformdirs==4.2.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
19-
pluggy==1.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
20-
pre-commit==3.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
21-
pycodestyle==2.11.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
22-
pyflakes==3.2.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
23-
pytest==7.4.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
24-
pyyaml==6.0.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
25-
respx==0.20.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
26-
setuptools==69.5.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
27-
sniffio==1.3.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
28-
tomli==2.0.1 ; python_full_version >= "3.8.1" and python_version < "3.11"
29-
typing-extensions==4.11.0 ; python_full_version >= "3.8.1" and python_version < "3.11"
30-
virtualenv==20.26.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
1+
anyio==4.6.1 ; python_version >= "3.9" and python_full_version < "4.0.0"
2+
certifi==2024.8.30 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
3+
cfgv==3.4.0 ; python_version >= "3.9" and python_full_version < "4.0.0"
4+
colorama==0.4.6 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0" and sys_platform == "win32"
5+
distlib==0.3.9 ; python_version >= "3.9" and python_full_version < "4.0.0"
6+
exceptiongroup==1.2.2 ; python_version >= "3.9" and python_version < "3.11"
7+
filelock==3.16.1 ; python_version >= "3.9" and python_full_version < "4.0.0"
8+
flake8==7.1.1 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
9+
h11==0.14.0 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
10+
httpcore==1.0.6 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
11+
httpx==0.27.2 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
12+
identify==2.6.1 ; python_version >= "3.9" and python_full_version < "4.0.0"
13+
idna==3.10 ; python_version >= "3.9" and python_full_version < "4.0.0"
14+
iniconfig==2.0.0 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
15+
mccabe==0.7.0 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
16+
nodeenv==1.9.1 ; python_version >= "3.9" and python_full_version < "4.0.0"
17+
packaging==24.1 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
18+
platformdirs==4.3.6 ; python_version >= "3.9" and python_full_version < "4.0.0"
19+
pluggy==1.5.0 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
20+
pre-commit==3.8.0 ; python_version >= "3.9" and python_full_version < "4.0.0"
21+
pycodestyle==2.12.1 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
22+
pyflakes==3.2.0 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
23+
pytest==8.3.3 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
24+
pyyaml==6.0.2 ; python_version >= "3.9" and python_full_version < "4.0.0"
25+
respx==0.21.1 ; python_full_version >= "3.9.0" and python_full_version < "4.0.0"
26+
sniffio==1.3.1 ; python_version >= "3.9" and python_full_version < "4.0.0"
27+
tomli==2.0.2 ; python_full_version >= "3.9.0" and python_version < "3.11"
28+
typing-extensions==4.12.2 ; python_version >= "3.9" and python_version < "3.11"
29+
virtualenv==20.26.6 ; python_version >= "3.9" and python_full_version < "4.0.0"

tests/test_client_library.py

+11-24
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,17 @@ def test_import_lab_from_path_virl(
7070
Lab.sync = Mock()
7171

7272
(tmp_path / "topology.virl").write_text("<?xml version='1.0' encoding='UTF-8'?>")
73-
with patch.object(Lab, "sync", autospec=True) as sync_mock:
74-
lab = cl.import_lab_from_path(path=(tmp_path / "topology.virl").as_posix())
73+
lab = cl.import_lab_from_path(path=(tmp_path / "topology.virl").as_posix())
7574

7675
assert lab.title is not None
7776
assert lab._url_for("lab").startswith("labs/")
7877

7978
cl._session.post.assert_called_once_with(
8079
"import/virl-1x",
80+
params=None,
8181
content="<?xml version='1.0' encoding='UTF-8'?>",
8282
)
8383
cl._session.post.assert_called_once()
84-
sync_mock.assert_called_once_with()
8584

8685

8786
@python37_or_newer
@@ -94,10 +93,9 @@ def test_import_lab_from_path_virl_title(
9493
Lab.sync = Mock()
9594
new_title = "new_title"
9695
(tmp_path / "topology.virl").write_text("<?xml version='1.0' encoding='UTF-8'?>")
97-
with patch.object(Lab, "sync", autospec=True):
98-
lab = cl.import_lab_from_path(
99-
path=(tmp_path / "topology.virl").as_posix(), title=new_title
100-
)
96+
lab = cl.import_lab_from_path(
97+
path=(tmp_path / "topology.virl").as_posix(), title=new_title
98+
)
10199
assert lab.title is not None
102100
assert lab._url_for("lab").startswith("labs/")
103101

@@ -142,15 +140,13 @@ def initial_different_response(initial, subsequent=httpx.Response(200)):
142140
while True:
143141
yield subsequent
144142

145-
# mock failed and successful authentication:
146-
respx.post(
147-
"https://0.0.0.0/fake_url/api/v0/authenticate"
148-
).side_effect = initial_different_response(
143+
# mock failed and successful authentication
144+
side_effect = initial_different_response(
149145
httpx.Response(403), httpx.Response(200, json="7bbcan78a98bch7nh3cm7hao3nc7")
150146
)
151-
respx.get(
152-
"https://0.0.0.0/fake_url/api/v0/authok"
153-
).side_effect = initial_different_response(httpx.Response(401))
147+
respx.post("https://0.0.0.0/fake_url/api/v0/authenticate").side_effect = side_effect
148+
side_effect = initial_different_response(httpx.Response(401))
149+
respx.get("https://0.0.0.0/fake_url/api/v0/authok").side_effect = side_effect
154150

155151
# mock get labs
156152
respx.get("https://0.0.0.0/fake_url/api/v0/labs").respond(json=[])
@@ -367,15 +363,6 @@ def test_client_library_str_and_repr(client_library_server_current):
367363
assert str(client_library) == "ClientLibrary URL: https://somehost/api/v0/"
368364

369365

370-
def test_major_version_mismatch(client_library_server_1_0_0):
371-
with pytest.raises(InitializationError) as err:
372-
ClientLibrary("somehost", "virl2", password="virl2")
373-
assert (
374-
str(err.value)
375-
== f"Major version mismatch. Client {CURRENT_VERSION}, controller 1.0.0."
376-
)
377-
378-
379366
def test_incompatible_version(client_library_server_2_0_0):
380367
with pytest.raises(InitializationError) as err:
381368
with patch.object(
@@ -714,7 +701,7 @@ def test_different_version_strings():
714701
Version("54dev0+build8.7ee86bf8")
715702

716703

717-
def test_import_lab_offline(
704+
def test_import_lab_offline_deprecated(
718705
client_library_server_current, mocked_session, tmp_path: Path, test_dir
719706
):
720707
client_library = ClientLibrary(

0 commit comments

Comments
 (0)