Examples
This page provides a few simple examples on how to use the VIRL 2 Client Library:
from virl2_client import ClientLibrary
client = ClientLibrary("https://192.168.1.1", "username", "password")
A custom SSL certificate bundle can be passed in ssl_verify:
client = ClientLibrary("https://192.168.1.1", "username", "password", ssl_verify="./cert.pem")
You can pass a certificate using the CA_BUNDLE
or CML_VERIFY_CERT
environment variables as well.
If no username or password are given then the environment will be checked,
looking for VIRL2_USER
or VIRL_USERNAME
and VIRL2_PASS
or VIRL_PASSWORD
, respectively.
Environment variables take precedence over those provided in arguments.
It’s also possible to pass the URL as an environment variable VIRL2_URL
or VIRL_HOST
.
Disabling SSL certificate verification (not recommended):
client = ClientLibrary("https://192.168.1.1", "username", "password", ssl_verify=False)
Creating a lab with nodes and links
This creates a lab, adds two nodes, interfaces and a link between them:
lab = client.create_lab()
r1 = lab.create_node("r1", "iosv", 50, 100)
r1.config = "hostname router1"
r2 = lab.create_node("r2", "iosv", 50, 200)
r2.config = "hostname router2"
# create a link between r1 and r2
r1_i1 = r1.create_interface()
r2_i1 = r2.create_interface()
lab.create_link(r1_i1, r2_i1)
# alternatively, use this convenience function:
lab.connect_two_nodes(r1, r2)
# start the lab
lab.start()
# print nodes and interfaces states:
for node in lab.nodes():
print(node, node.state, node.cpu_usage)
for interface in node.interfaces():
print(interface, interface.readpackets, interface.writepackets)
lab.stop()
lab.wipe()
lab.remove_node(r2)
lab.remove()
Stopping all the labs
This snippet loops over all labs and stops them:
for lab in client.all_labs():
lab.stop()
Getting all lab names
Get a list of all the lab names the user owns:
all_labs_names = [lab.title for lab in client.all_labs()]
Stopping all labs of a User
The following code loops over all labs the user owns, stops the lab, wipes the lab and then removes the lab from the controller:
lab_list = client.get_lab_list()
for lab_id in lab_list:
lab = client.join_existing_lab(lab_id)
lab.stop()
lab.wipe()
client.remove_lab(lab_id)
Uploading an image disk file
This shows how to upload a local disk file to the controller. It can then be used with to create a image definition for a given node type:
filename = "/Users/username/Desktop/vios-adventerprisek9-m.spa.158-3.m2.qcow2"
client_library.definitions.upload_image_file(filename, rename="iosv-test.qcow2")
Using the Client Library with Netmiko
The following example shows how the VIRL2 client library can be combined with Netmiko.
The code shows how to identify a XRv node in a specific lab and how to create crypto keys which require special handling as the creation is done in exec mode and is interactive:
import getpass
import netmiko
from virl2_client import ClientLibrary
LAB_USERNAME = 'cisco'
LAB_PASSWORD = 'cisco'
VIRL_CONTROLLER = 'cml2-controller'
VIRL_USERNAME = input('username: ')
VIRL_PASSWORD = getpass.getpass('password: ')
client = ClientLibrary(VIRL_CONTROLLER,
VIRL_USERNAME,
VIRL_PASSWORD,
ssl_verify=False)
# this assumes that there's exactly one lab with this title
our_lab = client.find_labs_by_title('my_lab')[0]
iosv_node = our_lab.get_node_by_label('iosv-0')
# open the Netmiko connection via the terminal server
# (SSH to the controller connects to the terminal server)
c = netmiko.ConnectHandler(device_type='terminal_server',
host=VIRL_CONTROLLER,
username=VIRL_USERNAME,
password=VIRL_PASSWORD)
# send CR, get a prompt on terminal server
c.write_channel('\r')
# open the connection to the console
c.write_channel(f'open /{our_lab.title}/{iosv_node.label}/0\r')
# router login
# this makes an assumption that it's required to login
c.write_channel('\r')
c.write_channel(LAB_USERNAME + '\r')
c.write_channel(LAB_PASSWORD + '\r')
# switch to Cisco IOS mode
netmiko.redispatch(c, device_type='cisco_ios')
c.find_prompt()
# get the list of interfaces
result = c.send_command('show ip int brief')
print(result)
# create the keys
c.write_channel('enable\r')
c.write_channel('configure terminal\r')
result = c.send_command('crypto key generate rsa',
expect_string='How many bits in the modulus \[512\]\: ')
print(result)
# send the key length
c.write_channel('2048\n')
# retrieve the result
c.write_channel('exit\r')
c.write_channel('disable\r')
result = c.send_command_timing('show crypto key mypubkey rsa', last_read=2.0)
print(result)
Licensing the System
The following example shows how to apply a license to the system using a token and retrieve licensing status using the the VIRL2 client library:
import getpass
import json
from virl2_client import ClientLibrary
VIRL_CONTROLLER = "cml2-controller"
VIRL_USERNAME = input("username: ")
VIRL_PASSWORD = getpass.getpass("password: ")
SL_TOKEN = input("smart license token: ")
PRODUCT_CONFIG = input("product configuration: ")
client = ClientLibrary(VIRL_CONTROLLER, VIRL_USERNAME, VIRL_PASSWORD, ssl_verify=False)
# Get the licensing handle from the client as a property
licensing = client.licensing
# Set the product configuration
licensing.set_product_license(PRODUCT_CONFIG)
# Setup default license transport (i.e., directly connected to the external
# Smart License server)
licensing.set_default_transport()
# Register with the Smart License server.
# Wait for registration and authorization to complete.
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)
)
exit(1)
# Get the current registration status.
# This returns a JSON blob with license status and authorization details.
status = licensing.status()
# Get the current list of licensed features.
# This returns a JSON blob with licensed features.
features = licensing.features()
print(json.dumps(status, indent=2))
print(json.dumps(features, indent=2))
The output for this would look something like the following:
{
"registration": {
"status": "COMPLETED",
"expires": "2021-06-10 20:17:39",
"smart_account": "Foo",
"virtual_account": "Bar",
"instance_name": "cml-controller.cml.lab",
"register_time": {
"succeeded": null,
"attempted": "2020-06-10 20:22:33",
"scheduled": null,
"status": null,
"failure": "OK",
"success": "SUCCESS"
},
"renew_time": {
"succeeded": null,
"attempted": null,
"scheduled": "2020-12-07 20:22:40",
"status": null,
"failure": null,
"success": "FAILED"
}
},
"authorization": {
"status": "IN_COMPLIANCE",
"renew_time": {
"succeeded": null,
"attempted": "2020-07-25 16:44:09",
"scheduled": "2020-08-24 16:44:08",
"status": "SUCCEEDED",
"failure": null,
"success": "SUCCESS"
},
"expires": "2020-10-23 16:39:07"
},
"features": [
{
"name": "CML - Enterprise License",
"description": "Cisco Modeling Labs - Enterprise License with 20 nodes capacity included",
"in_use": 1,
"status": "IN_COMPLIANCE",
"version": "1.0"
},
{
"name": "CML \u2013 Expansion Nodes",
"description": "Cisco Modeling Labs - Expansion node capacity for CML Enterprise Servers",
"in_use": 50,
"status": "IN_COMPLIANCE",
"version": "1.0"
}
]
"reservation_mode": false,
"transport": {
"ssms": "https://smartreceiver.cisco.com/licservice/license",
"proxy": {
"server": null,
"port": null
},
"default_ssms": "https://smartreceiver.cisco.com/licservice/license"
},
"udi": {
"hostname": "cml2-controller",
"product_uuid": "00000000-0000-4000-a000-000000000000"
},
"product_license": {
"active": "CML_Personal",
"is_enterprise": False
}
}
[
{
"id": "regid.2019-10.com.cisco.CML_ENT_BASE,1.0_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"name": "CML - Enterprise License",
"description": "Cisco Modeling Labs - Enterprise License with 20 nodes capacity included",
"in_use": 1,
"status": "IN_COMPLIANCE",
"version": "1.0",
"min": 0,
"max": 1
},
{
"id": "regid.2019-10.com.cisco.CML_NODE_COUNT,1.0_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"name": "CML \u2013 Expansion Nodes",
"description": "Cisco Modeling Labs - Expansion node capacity for CML Enterprise Servers",
"in_use": 50,
"status": "IN_COMPLIANCE",
"version": "1.0",
"min": 0,
"max": 300
}
]
This example can also be found in the examples
directory as licensing.py
.
Using Link Conditioning
The next example applies link conditioning to a link identified by the user. It
requires to provide a username, password and a labname. It will then list all
links inside of this lab. The user will then identify a link where the current
link condition will be shown first (or {}
, an empty JSON object if there’s
none applied). Then the user can enter new values or “None” if the condition
should be removed:
import getpass
import re
from requests.exceptions import HTTPError
from virl2_client import ClientLibrary
VIRL_CONTROLLER = "cml2-controller"
VIRL_USERNAME = input("username: ")
VIRL_PASSWORD = getpass.getpass("password: ")
LAB_NAME = input("enter lab name: ")
client = ClientLibrary(VIRL_CONTROLLER, VIRL_USERNAME, VIRL_PASSWORD, ssl_verify=False)
# Find the lab by title and join it as long as it's the only
# lab with that title.
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))
exit(1)
lobj = client.join_existing_lab(labs[0].id)
if not lobj:
print("ERROR: Failed to join lab {}".format(LAB_NAME))
exit(1)
# Print all links in the lab and ask which link to condition.
i = 1
liobjs = []
for link in lobj.links():
print(
"{}. {}[{}] <-> {}[{}]".format(
i,
link.interface_a.node.label,
link.interface_a.label,
link.interface_b.node.label,
link.interface_b.label,
)
)
liobjs.append(lobj.get_link_by_interfaces(link.interface_a, link.interface_b))
i += 1
print()
lnum = 0
while lnum < 1 or lnum > i:
lnum = input("enter link number to condition (1-{}): ".format(i-1))
try:
lnum = int(lnum)
except ValueError:
lnum = 0
# Print the selected link's current conditioning (if any).
link = liobjs[lnum-1]
print("Current condition is {}".format(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(
"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":
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
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)
print("Link conditioning set.")
except HTTPError as exc:
print("ERROR: Failed to set link conditioning: {}", format(exc))
exit(1)
This example can also be found in the examples
directory as link_conditioning.py
.