Skip to content

Commit

Permalink
Release/v2.7.0 (#92)
Browse files Browse the repository at this point in the history
* 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)
  • Loading branch information
tmikuska committed Mar 28, 2024
1 parent a4e2fd5 commit 5bf320f
Show file tree
Hide file tree
Showing 40 changed files with 5,743 additions and 2,411 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# https://github.com/python-poetry/poetry/issues/3160
# when resolved, we should be able to run with hashes
tests/requirements.txt: poetry.lock
poetry export --format=requirements.txt --dev --without-hashes --output=$@
poetry export --format=requirements.txt --with dev --without-hashes --output=$@

clean:
rm -rf dist virl2_client.egg-info .built .pytest_cache .coverage coverage.xml
Expand Down
2 changes: 1 addition & 1 deletion NOTICE
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Python bindings for the Cisco VIRL 2 Network Simulation Platform

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

This project includes software developed at Cisco Systems, Inc. and/or its affiliates.
13 changes: 5 additions & 8 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#
# This file is part of VIRL 2
# Copyright (c) 2019-2023, Cisco Systems, Inc.
# Copyright (c) 2019-2024, Cisco Systems, Inc.
# All rights reserved.
#
#
Expand All @@ -21,8 +21,7 @@

from __future__ import annotations

import os
from typing import Iterator
from pathlib import Path

import pytest

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


@pytest.fixture(scope="function")
def change_test_dir(request: pytest.FixtureRequest) -> Iterator[None]:
os.chdir(request.path.parent)
yield
os.chdir(request.config.invocation_params.dir)
@pytest.fixture
def test_dir(request: pytest.FixtureRequest) -> Path:
return request.path.parent
8 changes: 4 additions & 4 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# This file is part of VIRL 2
# Copyright (c) 2019-2023, Cisco Systems, Inc.
# Copyright (c) 2019-2024, Cisco Systems, Inc.
# All rights reserved.
#
# Configuration file for the Sphinx documentation builder.
Expand All @@ -25,7 +25,7 @@
# -- Project information -----------------------------------------------------

project = "virl2_client"
copyright = "Copyright (c) 2019-2022, Cisco Systems, Inc."
copyright = "Copyright (c) 2019-2024, Cisco Systems, Inc."
author = "VIRL2 team <virl@cisco.com>"

# The short X.Y version
Expand Down Expand Up @@ -176,7 +176,7 @@
# Bibliographic Dublin Core info.
epub_title = project

# The unique identifier of the text. This can be a ISBN number
# The unique identifier of the text. This can be an ISBN
# or the project homepage.
#
# epub_identifier = ''
Expand All @@ -194,7 +194,7 @@
# 'member-order': 'bysource',
"member-order": "groupwise",
"special-members": "__init__",
# "ignore-module-all": None,
"ignore-module-all": True,
# 'undoc-members': None,
# 'exclude-members': '__weakref__'
}
Expand Down
1 change: 0 additions & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ CML 2 controller.

intro
examples
api/*

.. only:: internal

Expand Down
2 changes: 0 additions & 2 deletions examples/demo.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
"metadata": {},
"outputs": [],
"source": [
"import urllib3\n",
"from virl2_client import ClientLibrary\n",
"\n",
"urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)\n",
"client = ClientLibrary(\"https://192.168.1.1\", \"virl2\", \"virl2\", ssl_verify=False)"
]
},
Expand Down
4 changes: 2 additions & 2 deletions examples/licensing.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
#
# This file is part of VIRL 2
# Copyright (c) 2019-2023, Cisco Systems, Inc.
# Copyright (c) 2019-2024, Cisco Systems, Inc.
# All rights reserved.
#
# Python bindings for the Cisco VIRL 2 Network Simulation Platform
Expand Down Expand Up @@ -43,7 +43,7 @@
result = licensing.register_wait(SL_TOKEN)
if not result:
result = licensing.get_reservation_return_code()
print("ERROR: Failed to register with Smart License server: {}!".format(result))
print(f"ERROR: Failed to register with Smart License server: {result}!")
exit(1)

# Get the current registration status. This returns a JSON blob with license
Expand Down
51 changes: 22 additions & 29 deletions examples/link_conditioning.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
#
# This file is part of VIRL 2
# Copyright (c) 2019-2023, Cisco Systems, Inc.
# Copyright (c) 2019-2024, Cisco Systems, Inc.
# All rights reserved.
#
# Python bindings for the Cisco VIRL 2 Network Simulation Platform
Expand All @@ -21,7 +21,6 @@


import getpass
import re

from httpx import HTTPStatusError

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

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

lobj = client.join_existing_lab(labs[0].id)
lab = client.join_existing_lab(labs[0].id)

if not lobj:
print("ERROR: Failed to join lab {}".format(LAB_NAME))
if not lab:
print(f"ERROR: Failed to join lab {LAB_NAME}")
exit(1)

# Print all links in the lab and ask which link to condition.
i = 1
liobjs = []
for link in lobj.links():
links = []
for link in lab.links():
print(
"{}. {}[{}] <-> {}[{}]".format(
i,
link.interface_a.node.label,
link.interface_a.label,
link.interface_b.node.label,
link.interface_b.label,
)
f"{i}. {link.interface_a.node.label}[{link.interface_a.label}] <-> "
f"{link.interface_b.node.label}[{link.interface_b.label}]"
)
liobjs.append(lobj.get_link_by_interfaces(link.interface_a, link.interface_b))
links.append(link.interface_a.get_link_to(link.interface_b))
i += 1

print()
lnum = 0
while lnum < 1 or lnum > i:
lnum = input("Enter link number to condition (1-{}): ".format(i))
link_number = 0
while link_number < 1 or link_number > i:
try:
lnum = int(lnum)
link_number = int(input(f"Enter link number to condition (1-{i}): "))
except ValueError:
lnum = 0
link_number = 0

# Print the selected link's current conditioning (if any).
link = liobjs[lnum - 1]
print("Current condition is {}".format(link.get_condition()))
link = links[link_number - 1]
print(f"Current condition is {link.get_condition()}")
# Request the new conditoning for bandwidth, latency, jitter, and loss.
# Bandwidth is an integer between 0-10000000 kbps
# Bandwidth of 0 is "no bandwidth restriction"
# Latency is an integer between 0-10000 ms
# Jitter is an integer between 0-10000 ms
# Loss is a float between 0-100%
new_cond = input(
new_condition = input(
"enter new condition in format 'BANDWIDTH, "
"LATENCY, JITTER, LOSS' or 'None' to disable: "
)
# If "None" is provided disable any conditioning on the link.
if new_cond.lower() == "none":
if new_condition.lower() == "none":
link.remove_condition()
print("Link conditioning has been disabled.")
else:
try:
# Set the current conditioning based on the provided values.
cond_list = re.split(r"\s*,\s*", new_cond)
bw = int(cond_list[0]) # Bandwidth is an int
cond_list = new_condition.split(",")
bandwidth = int(cond_list[0]) # Bandwidth is an int
latency = int(cond_list[1]) # Latency is an int
jitter = int(cond_list[2]) # Jitter is an int
loss = float(cond_list[3]) # Loss is a float
link.set_condition(bw, latency, jitter, loss)
link.set_condition(bandwidth, latency, jitter, loss)
print("Link conditioning set.")
except HTTPStatusError as exc:
print("ERROR: Failed to set link conditioning: {}", format(exc))
print(f"ERROR: Failed to set link conditioning: {exc}")
exit(1)
130 changes: 68 additions & 62 deletions examples/sample.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
#
# This file is part of VIRL 2
# Copyright (c) 2019-2023, Cisco Systems, Inc.
# Copyright (c) 2019-2024, Cisco Systems, Inc.
# All rights reserved.
#
# Python bindings for the Cisco VIRL 2 Network Simulation Platform
Expand All @@ -19,22 +19,11 @@
# limitations under the License.
#

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

CERT = """-----BEGIN CERTIFICATE-----
MIIDGzCCAgOgAwIBAgIBATANBgkqhkiG9w0BAQsFADAvMQ4wDAYDVQQKEwVDaXNj
WhcNMzMwNDI0MjE1NTQzWjAvMQ4wDAYDVQQKEwVDaXNjbzEdMBsGA1UEAxMUTGlj
ZW5zaW5nIFJvb3QgLSBERVYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCcVnEB1h7fLrzDunrg27JBs7QyipsA64qA0Cqob17xrr/etnvWrX2te0P1gnU7
/8wcpaeEGgdpNNOvmQeO9heRlvpPs/LtOULHVr8coKnMmKen+eQ3JNnmHUeJ6eeS
3Z8ntFF8K97Q61uaeHughdm78APwVjvgpEUMjxJ7VYM+vBOFLZutmGjTrgdcJ5h8
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRDIUUhtfshehpNG7cCNuZky+yLZTANBgkq
hkiG9w0BAQsFAAOCAQEAhfGx8q6ufS+cqwNRwynj8a6YLfEfXjyQ9gCzze1aFJH7
3wfgbKoPQyWftMSuIID2dYw7esVOqqA+xbUKL2cK/4ftpkYvz8Q5Z8AkqzLuPM3P
oEudrhu6u9rI31WHz1HLHABaKC+LUYpajG+bPKq6NEYy7zp1wvRUUHqbz9MMi+VK
EYct4M8SANDRAY/ZrGhZaBZ+Qhybw5Ttm8hUY4OygUYHsr3t38FgW00WAHtocj4l
z1LPIlCn0j76n2sH+w9jhp3MO7xlJQaTOM9rpsuO/Q==
-----END CERTIFICATE-----"""
from virl2_client import ClientLibrary

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

Expand All @@ -45,55 +34,72 @@
)


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

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

# Set up licensing configuration
client_library.licensing.set_transport(ssms=SSMS)
client_library.licensing.register_wait(token=TOKEN)

lab_list = cl.get_lab_list()
# Get a list of existing labs and print their details
lab_list = client_library.get_lab_list()
for lab_id in lab_list:
lab = cl.join_existing_lab(lab_id)
lab.stop()
lab.wipe()
cl.remove_lab(lab_id)

lab = cl.create_lab()
lab = cl.join_existing_lab(lab_id="lab_1")

s1 = lab.create_node("s1", "server", 50, 100)
s2 = lab.create_node("s2", "server", 50, 200)
print(s1, s2)

# create a link between s1 and s2, equivalent to
# s1_i1 = s1.create_interface()
# s2_i1 = s2.create_interface()
# lab.create_link(s1_i1, s2_i1)
lab.connect_two_nodes(s1, s2)

# this must remove the link between s1 and s2
lab.remove_node(s2)

lab = client_library.join_existing_lab(lab_id)
print("Lab ID:", lab.id)
print("Lab Title:", lab.title)
print("Lab Description:", lab.description)
print("Lab State:", lab.state)
print("----")

# A simpler way to join all labs at once
labs = client_library.all_labs()

# Create a lab
lab = client_library.create_lab()

# Create two server nodes
server1 = lab.create_node("server1", "server", 50, 100)
server2 = lab.create_node("server2", "server", 50, 200)
print("Created nodes:", server1, server2)

# Create a link between server1 and server2
link = lab.connect_two_nodes(server1, server2)
print("Created link between server1 and server2")

# Remove the link between server1 and server2
link.remove()
print("Removed link between server1 and server2")

# Manually synchronize lab states - this happens automatically once per second
# by default, but we can skip the wait by calling this method
lab.sync_states()

# Print the state of each node and its interfaces
for node in lab.nodes():
print(node, node.state)
for iface in node.interfaces():
print(iface, iface.state)

assert [link for link in lab.links() if link.state is not None] == []

# in case you's like to deregister after you're done
status = cl.licensing.deregister()
cl.licensing.remove_certificate()
# set licensing back to default transport
# default ssms is "https://smartreceiver.cisco.com/licservice/license"
cl.licensing.set_default_transport()
print(f"Node: {node.label} | State: {node.state}")
for interface in node.interfaces():
print(f" Interface: {interface.label} | State: {interface.state}")

# Export a lab topology to a file
lab_data = lab.download()
with open("demo_lab_export.yaml", "w") as file:
file.write(lab_data)
print("Lab exported successfully.")

# Clean up the lab
lab.stop()
lab.wipe()
lab.remove() # or client_library.remove_lab(lab_id)

# Deregister (optional) and set licensing back to the default transport (optional)
# Default SSMS is "https://smartreceiver.cisco.com/licservice/license"
client_library.licensing.deregister()
client_library.licensing.set_default_transport()
Loading

0 comments on commit 5bf320f

Please sign in to comment.