Skip to content

Commit 5bf320f

Browse files
authored
Release/v2.7.0 (#92)
* SIMPLE-5780 replace change test dir fixture (#54) * SIMPLE-5377 PCL general improvements (#59) * SIMPLE-5953 pcl update for smbios parameters (#61) * SIMPLE-5974 Support for locked_compute_id (#64) * SIMPLE-5806 PCL support for multiple config files (#60) * SIMPLE-5806 fix for a missed bug (#65) * SIMPLE-5949 Deprecated licensing certificate support in PCL (#66) * updated pyproject.toml and poetry.lock; versions: poetry-1.4, lock for… (#68) * SIMPLE-6084 Fixed regressions in create_node method to preserve compatibility with older CML releases * SIMPLE-6020 sped up syncing operational by removing unneeded API calls (#72) * SIMPLE-5747 added .iol files to list of accepted image files (#71) * SIMPLE-5829 Backported fix for missing image extension * SIMPLE-6087 creating a lab no longer raises a warning (#74) * SIMPLE-6105 node.remove no longer always raises NodeNotFound (#75) * SIMPLE-6073: Avoid using unneeded pyats features when parsing our generated testbed file, which uses none of them. Refactor checking whether pyats is installed. (#76) * SIMPLE-6168 Added missing copyright info, fixed invalid copyright year * SIMPLE-6204 rename locked compute to pinned (#79) * SIMPLE-6093 Implemented controller property (#82) * SIMPLE-6285 Improvements to multiple node configuration API (#83) * SIMPLE-6124 Annotations support (#85) * SIMPLE-6428 Fixed annotation update on lab sync (#89)
1 parent a4e2fd5 commit 5bf320f

40 files changed

+5743
-2411
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# https://github.com/python-poetry/poetry/issues/3160
66
# when resolved, we should be able to run with hashes
77
tests/requirements.txt: poetry.lock
8-
poetry export --format=requirements.txt --dev --without-hashes --output=$@
8+
poetry export --format=requirements.txt --with dev --without-hashes --output=$@
99

1010
clean:
1111
rm -rf dist virl2_client.egg-info .built .pytest_cache .coverage coverage.xml

NOTICE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Python bindings for the Cisco VIRL 2 Network Simulation Platform
22

3-
Copyright (c) 2019-2022, Cisco Systems, Inc. and/or its affiliates
3+
Copyright (c) 2019-2024, Cisco Systems, Inc. and/or its affiliates
44

55
This project includes software developed at Cisco Systems, Inc. and/or its affiliates.

conftest.py

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#
22
# This file is part of VIRL 2
3-
# Copyright (c) 2019-2023, Cisco Systems, Inc.
3+
# Copyright (c) 2019-2024, Cisco Systems, Inc.
44
# All rights reserved.
55
#
66
#
@@ -21,8 +21,7 @@
2121

2222
from __future__ import annotations
2323

24-
import os
25-
from typing import Iterator
24+
from pathlib import Path
2625

2726
import pytest
2827

@@ -47,8 +46,6 @@ def pytest_addoption(
4746
parser.addini("asyncio_mode", "suppress the warning")
4847

4948

50-
@pytest.fixture(scope="function")
51-
def change_test_dir(request: pytest.FixtureRequest) -> Iterator[None]:
52-
os.chdir(request.path.parent)
53-
yield
54-
os.chdir(request.config.invocation_params.dir)
49+
@pytest.fixture
50+
def test_dir(request: pytest.FixtureRequest) -> Path:
51+
return request.path.parent

docs/source/conf.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
#
33
# This file is part of VIRL 2
4-
# Copyright (c) 2019-2023, Cisco Systems, Inc.
4+
# Copyright (c) 2019-2024, Cisco Systems, Inc.
55
# All rights reserved.
66
#
77
# Configuration file for the Sphinx documentation builder.
@@ -25,7 +25,7 @@
2525
# -- Project information -----------------------------------------------------
2626

2727
project = "virl2_client"
28-
copyright = "Copyright (c) 2019-2022, Cisco Systems, Inc."
28+
copyright = "Copyright (c) 2019-2024, Cisco Systems, Inc."
2929
author = "VIRL2 team <[email protected]>"
3030

3131
# The short X.Y version
@@ -176,7 +176,7 @@
176176
# Bibliographic Dublin Core info.
177177
epub_title = project
178178

179-
# The unique identifier of the text. This can be a ISBN number
179+
# The unique identifier of the text. This can be an ISBN
180180
# or the project homepage.
181181
#
182182
# epub_identifier = ''
@@ -194,7 +194,7 @@
194194
# 'member-order': 'bysource',
195195
"member-order": "groupwise",
196196
"special-members": "__init__",
197-
# "ignore-module-all": None,
197+
"ignore-module-all": True,
198198
# 'undoc-members': None,
199199
# 'exclude-members': '__weakref__'
200200
}

docs/source/index.rst

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

1515
intro
1616
examples
17-
api/*
1817

1918
.. only:: internal
2019

examples/demo.ipynb

-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
"metadata": {},
77
"outputs": [],
88
"source": [
9-
"import urllib3\n",
109
"from virl2_client import ClientLibrary\n",
1110
"\n",
12-
"urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)\n",
1311
"client = ClientLibrary(\"https://192.168.1.1\", \"virl2\", \"virl2\", ssl_verify=False)"
1412
]
1513
},

examples/licensing.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python3
22
#
33
# This file is part of VIRL 2
4-
# Copyright (c) 2019-2023, Cisco Systems, Inc.
4+
# Copyright (c) 2019-2024, Cisco Systems, Inc.
55
# All rights reserved.
66
#
77
# Python bindings for the Cisco VIRL 2 Network Simulation Platform
@@ -43,7 +43,7 @@
4343
result = licensing.register_wait(SL_TOKEN)
4444
if not result:
4545
result = licensing.get_reservation_return_code()
46-
print("ERROR: Failed to register with Smart License server: {}!".format(result))
46+
print(f"ERROR: Failed to register with Smart License server: {result}!")
4747
exit(1)
4848

4949
# Get the current registration status. This returns a JSON blob with license

examples/link_conditioning.py

+22-29
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python3
22
#
33
# This file is part of VIRL 2
4-
# Copyright (c) 2019-2023, Cisco Systems, Inc.
4+
# Copyright (c) 2019-2024, Cisco Systems, Inc.
55
# All rights reserved.
66
#
77
# Python bindings for the Cisco VIRL 2 Network Simulation Platform
@@ -21,7 +21,6 @@
2121

2222

2323
import getpass
24-
import re
2524

2625
from httpx import HTTPStatusError
2726

@@ -39,67 +38,61 @@
3938
labs = client.find_labs_by_title(LAB_NAME)
4039

4140
if not labs or len(labs) != 1:
42-
print("ERROR: Unable to find a unique lab named {}".format(LAB_NAME))
41+
print(f"ERROR: Unable to find a unique lab named {LAB_NAME}")
4342
exit(1)
4443

45-
lobj = client.join_existing_lab(labs[0].id)
44+
lab = client.join_existing_lab(labs[0].id)
4645

47-
if not lobj:
48-
print("ERROR: Failed to join lab {}".format(LAB_NAME))
46+
if not lab:
47+
print(f"ERROR: Failed to join lab {LAB_NAME}")
4948
exit(1)
5049

5150
# Print all links in the lab and ask which link to condition.
5251
i = 1
53-
liobjs = []
54-
for link in lobj.links():
52+
links = []
53+
for link in lab.links():
5554
print(
56-
"{}. {}[{}] <-> {}[{}]".format(
57-
i,
58-
link.interface_a.node.label,
59-
link.interface_a.label,
60-
link.interface_b.node.label,
61-
link.interface_b.label,
62-
)
55+
f"{i}. {link.interface_a.node.label}[{link.interface_a.label}] <-> "
56+
f"{link.interface_b.node.label}[{link.interface_b.label}]"
6357
)
64-
liobjs.append(lobj.get_link_by_interfaces(link.interface_a, link.interface_b))
58+
links.append(link.interface_a.get_link_to(link.interface_b))
6559
i += 1
6660

6761
print()
68-
lnum = 0
69-
while lnum < 1 or lnum > i:
70-
lnum = input("Enter link number to condition (1-{}): ".format(i))
62+
link_number = 0
63+
while link_number < 1 or link_number > i:
7164
try:
72-
lnum = int(lnum)
65+
link_number = int(input(f"Enter link number to condition (1-{i}): "))
7366
except ValueError:
74-
lnum = 0
67+
link_number = 0
7568

7669
# Print the selected link's current conditioning (if any).
77-
link = liobjs[lnum - 1]
78-
print("Current condition is {}".format(link.get_condition()))
70+
link = links[link_number - 1]
71+
print(f"Current condition is {link.get_condition()}")
7972
# Request the new conditoning for bandwidth, latency, jitter, and loss.
8073
# Bandwidth is an integer between 0-10000000 kbps
8174
# Bandwidth of 0 is "no bandwidth restriction"
8275
# Latency is an integer between 0-10000 ms
8376
# Jitter is an integer between 0-10000 ms
8477
# Loss is a float between 0-100%
85-
new_cond = input(
78+
new_condition = input(
8679
"enter new condition in format 'BANDWIDTH, "
8780
"LATENCY, JITTER, LOSS' or 'None' to disable: "
8881
)
8982
# If "None" is provided disable any conditioning on the link.
90-
if new_cond.lower() == "none":
83+
if new_condition.lower() == "none":
9184
link.remove_condition()
9285
print("Link conditioning has been disabled.")
9386
else:
9487
try:
9588
# Set the current conditioning based on the provided values.
96-
cond_list = re.split(r"\s*,\s*", new_cond)
97-
bw = int(cond_list[0]) # Bandwidth is an int
89+
cond_list = new_condition.split(",")
90+
bandwidth = int(cond_list[0]) # Bandwidth is an int
9891
latency = int(cond_list[1]) # Latency is an int
9992
jitter = int(cond_list[2]) # Jitter is an int
10093
loss = float(cond_list[3]) # Loss is a float
101-
link.set_condition(bw, latency, jitter, loss)
94+
link.set_condition(bandwidth, latency, jitter, loss)
10295
print("Link conditioning set.")
10396
except HTTPStatusError as exc:
104-
print("ERROR: Failed to set link conditioning: {}", format(exc))
97+
print(f"ERROR: Failed to set link conditioning: {exc}")
10598
exit(1)

examples/sample.py

+68-62
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python3
22
#
33
# This file is part of VIRL 2
4-
# Copyright (c) 2019-2023, Cisco Systems, Inc.
4+
# Copyright (c) 2019-2024, Cisco Systems, Inc.
55
# All rights reserved.
66
#
77
# Python bindings for the Cisco VIRL 2 Network Simulation Platform
@@ -19,22 +19,11 @@
1919
# limitations under the License.
2020
#
2121

22-
from virl2_client import ClientLibrary
22+
# This script demonstrates various functionalities of the client library.
23+
# Each example is accompanied by comments explaining its purpose and usage.
24+
import pathlib
2325

24-
CERT = """-----BEGIN CERTIFICATE-----
25-
MIIDGzCCAgOgAwIBAgIBATANBgkqhkiG9w0BAQsFADAvMQ4wDAYDVQQKEwVDaXNj
26-
WhcNMzMwNDI0MjE1NTQzWjAvMQ4wDAYDVQQKEwVDaXNjbzEdMBsGA1UEAxMUTGlj
27-
ZW5zaW5nIFJvb3QgLSBERVYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
28-
AQCcVnEB1h7fLrzDunrg27JBs7QyipsA64qA0Cqob17xrr/etnvWrX2te0P1gnU7
29-
/8wcpaeEGgdpNNOvmQeO9heRlvpPs/LtOULHVr8coKnMmKen+eQ3JNnmHUeJ6eeS
30-
3Z8ntFF8K97Q61uaeHughdm78APwVjvgpEUMjxJ7VYM+vBOFLZutmGjTrgdcJ5h8
31-
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRDIUUhtfshehpNG7cCNuZky+yLZTANBgkq
32-
hkiG9w0BAQsFAAOCAQEAhfGx8q6ufS+cqwNRwynj8a6YLfEfXjyQ9gCzze1aFJH7
33-
3wfgbKoPQyWftMSuIID2dYw7esVOqqA+xbUKL2cK/4ftpkYvz8Q5Z8AkqzLuPM3P
34-
oEudrhu6u9rI31WHz1HLHABaKC+LUYpajG+bPKq6NEYy7zp1wvRUUHqbz9MMi+VK
35-
EYct4M8SANDRAY/ZrGhZaBZ+Qhybw5Ttm8hUY4OygUYHsr3t38FgW00WAHtocj4l
36-
z1LPIlCn0j76n2sH+w9jhp3MO7xlJQaTOM9rpsuO/Q==
37-
-----END CERTIFICATE-----"""
26+
from virl2_client import ClientLibrary
3827

3928
SSMS = "https://sch-alpha.cisco.com/its/service/oddce/services/DDCEService"
4029

@@ -45,55 +34,72 @@
4534
)
4635

4736

48-
# setup the connection and clean everything
49-
cl = ClientLibrary("http://localhost:8001", "cml2", "cml2cml2", allow_http=True)
50-
cl.is_system_ready(wait=True)
37+
# Set up the CML 2 connection
38+
server_url = "http://localhost:8001"
39+
username = "cml2" # Default username if not changed in CML instance
40+
password = pathlib.Path("/etc/machine-id").read_text().strip()
41+
# Default password is equal to the contents of the CML instances /etc/machine-id file
42+
# If you are running this script remotely, replace the password above
43+
client_library = ClientLibrary(server_url, username, password, allow_http=True)
5144

52-
# set transport if needed - also proxy can be set if needed
53-
# cl.licensing.licensing.set_transport(
54-
# ssms=ssms, proxy_server="172.16.1.100", proxy_port=8888
55-
# )
56-
cl.licensing.set_transport(ssms=SSMS)
57-
cl.licensing.install_certificate(cert=CERT)
58-
# 'register_wait' method waits max 45s for registration status to become COMPLETED
59-
# and another 45s for authorization status to become IN_COMPLIANCE
60-
cl.licensing.register_wait(token=TOKEN)
45+
# Check if the CML 2 system is ready
46+
client_library.is_system_ready(wait=True)
6147

48+
# Set up licensing configuration
49+
client_library.licensing.set_transport(ssms=SSMS)
50+
client_library.licensing.register_wait(token=TOKEN)
6251

63-
lab_list = cl.get_lab_list()
52+
# Get a list of existing labs and print their details
53+
lab_list = client_library.get_lab_list()
6454
for lab_id in lab_list:
65-
lab = cl.join_existing_lab(lab_id)
66-
lab.stop()
67-
lab.wipe()
68-
cl.remove_lab(lab_id)
69-
70-
lab = cl.create_lab()
71-
lab = cl.join_existing_lab(lab_id="lab_1")
72-
73-
s1 = lab.create_node("s1", "server", 50, 100)
74-
s2 = lab.create_node("s2", "server", 50, 200)
75-
print(s1, s2)
76-
77-
# create a link between s1 and s2, equivalent to
78-
# s1_i1 = s1.create_interface()
79-
# s2_i1 = s2.create_interface()
80-
# lab.create_link(s1_i1, s2_i1)
81-
lab.connect_two_nodes(s1, s2)
82-
83-
# this must remove the link between s1 and s2
84-
lab.remove_node(s2)
85-
55+
lab = client_library.join_existing_lab(lab_id)
56+
print("Lab ID:", lab.id)
57+
print("Lab Title:", lab.title)
58+
print("Lab Description:", lab.description)
59+
print("Lab State:", lab.state)
60+
print("----")
61+
62+
# A simpler way to join all labs at once
63+
labs = client_library.all_labs()
64+
65+
# Create a lab
66+
lab = client_library.create_lab()
67+
68+
# Create two server nodes
69+
server1 = lab.create_node("server1", "server", 50, 100)
70+
server2 = lab.create_node("server2", "server", 50, 200)
71+
print("Created nodes:", server1, server2)
72+
73+
# Create a link between server1 and server2
74+
link = lab.connect_two_nodes(server1, server2)
75+
print("Created link between server1 and server2")
76+
77+
# Remove the link between server1 and server2
78+
link.remove()
79+
print("Removed link between server1 and server2")
80+
81+
# Manually synchronize lab states - this happens automatically once per second
82+
# by default, but we can skip the wait by calling this method
8683
lab.sync_states()
84+
85+
# Print the state of each node and its interfaces
8786
for node in lab.nodes():
88-
print(node, node.state)
89-
for iface in node.interfaces():
90-
print(iface, iface.state)
91-
92-
assert [link for link in lab.links() if link.state is not None] == []
93-
94-
# in case you's like to deregister after you're done
95-
status = cl.licensing.deregister()
96-
cl.licensing.remove_certificate()
97-
# set licensing back to default transport
98-
# default ssms is "https://smartreceiver.cisco.com/licservice/license"
99-
cl.licensing.set_default_transport()
87+
print(f"Node: {node.label} | State: {node.state}")
88+
for interface in node.interfaces():
89+
print(f" Interface: {interface.label} | State: {interface.state}")
90+
91+
# Export a lab topology to a file
92+
lab_data = lab.download()
93+
with open("demo_lab_export.yaml", "w") as file:
94+
file.write(lab_data)
95+
print("Lab exported successfully.")
96+
97+
# Clean up the lab
98+
lab.stop()
99+
lab.wipe()
100+
lab.remove() # or client_library.remove_lab(lab_id)
101+
102+
# Deregister (optional) and set licensing back to the default transport (optional)
103+
# Default SSMS is "https://smartreceiver.cisco.com/licservice/license"
104+
client_library.licensing.deregister()
105+
client_library.licensing.set_default_transport()

0 commit comments

Comments
 (0)