Skip to content

Commit

Permalink
Merge pull request #19 from cisco-open/dev-2.0.12
Browse files Browse the repository at this point in the history
Dev 2.0.12
  • Loading branch information
tzarski0 authored Jun 17, 2024
2 parents 9a8aed3 + 78fd6ad commit 90b7f21
Show file tree
Hide file tree
Showing 13 changed files with 218 additions and 111 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# Catalyst SD-WAN Lab 2.0.10 [May 13, 2024]
# Catalyst SD-WAN Lab 2.0.12 [Jun 18, 2024]

- Added CML PATty support
- In delete task, print lab name when asking user to confirm if lab should be deleted
- In add task, added check to avoid deploying labs with duplicate names (although CML allows labs with duplicate names, this creates confusion for other tasks where lab name is used)

# Catalyst SD-WAN Lab 2.0.11 [May 13, 2024]

- Added sign task

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ The easiest way to run the tool is to provide all the lab variables in the rc fi

Note that if password was not defined, the user will be prompted for a password.

Note that MANAGER_IP can be:
- an IP address: SD-WAN Manager will be reachable over this IP address. By default the IP address should come from the same subnet as CML IP, unless custom bridge is specified during deploy task.
- a PATty port in format "pat:<outside-port>": SD-WAN Manager will be reachable over CML IP port <outside-port>. Before using this option, PATTy needs to be enabled on the CML server as per [CML documentation](https://developer.cisco.com/docs/modeling-labs/patty-tool-overview/).

### Task-specific Parameters
Task-specific parameters and options are defined after the task is provided. Each task has its own set of parameters. Check the task documentation to learn more about task-specific parameters.

Expand Down
46 changes: 42 additions & 4 deletions catalyst_sdwan_lab/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@

import argparse
import logging
import re
import sys
from typing import Tuple

import urllib3
from cisco_sdwan.tasks.utils import EnvVar, PromptArg, non_empty_type
Expand Down Expand Up @@ -531,10 +533,14 @@ def main() -> None:
if cli_args.task == "setup":
setup.main(cml, cli_args.loglevel, cli_args.list)
elif cli_args.task == "deploy":
patty_used, manager_ip, manager_port = set_manager_details(
cli_args.cml, cli_args.manager
)
deploy.main(
cml,
cli_args.cml,
cli_args.manager,
manager_ip,
manager_port,
cli_args.mmask,
cli_args.mgateway,
cli_args.muser,
Expand All @@ -543,15 +549,20 @@ def main() -> None:
cli_args.lab,
cli_args.bridge,
cli_args.dns,
patty_used,
cli_args.retry,
cli_args.loglevel,
)
elif cli_args.task == "add":
patty_used, manager_ip, manager_port = set_manager_details(
cli_args.cml, cli_args.manager
)
add.main(
cml,
cli_args.user,
cli_args.password,
cli_args.manager,
manager_ip,
manager_port,
cli_args.muser,
cli_args.mpassword,
cli_args.lab,
Expand All @@ -561,28 +572,37 @@ def main() -> None:
cli_args.loglevel,
)
elif cli_args.task == "backup":
patty_used, manager_ip, manager_port = set_manager_details(
cli_args.cml, cli_args.manager
)
backup.main(
cml,
cli_args.user,
cli_args.password,
cli_args.manager,
manager_ip,
manager_port,
cli_args.muser,
cli_args.mpassword,
cli_args.lab,
cli_args.workdir,
cli_args.loglevel,
)
elif cli_args.task == "restore":
patty_used, manager_ip, manager_port = set_manager_details(
cli_args.cml, cli_args.manager
)
restore.main(
cml,
cli_args.cml,
cli_args.manager,
manager_ip,
manager_port,
cli_args.mmask,
cli_args.mgateway,
cli_args.muser,
cli_args.mpassword,
cli_args.workdir,
cli_args.lab,
patty_used,
cli_args.deleteexisting,
cli_args.retry,
cli_args.loglevel,
Expand All @@ -603,5 +623,23 @@ def verify_cml_version(cml: ClientLibrary) -> None:
exit("Upgrade CML to 2.6 or later to use the tool.")


def set_manager_details(cml_ip: str, manager_ip: str) -> Tuple[bool, str, int]:
patty_used = False
manager_port = 443
if manager_ip.startswith("pat:"):
# PATty should be used for SD-WAN Manager reachability.
patty_used = True
manager_port_search = re.search(r"pat:(\d+)", manager_ip)
if manager_port_search:
manager_port = int(manager_port_search.group(1))
else:
exit(
"Wrong PATty configuration for manager_ip (expected pat:<outside_port>)."
)
manager_ip = cml_ip

return patty_used, manager_ip, manager_port


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ nodes:
label: Manager0{{ manager_num }}
node_definition: cat-sdwan-manager
ram: null
{% if patty_used %}
tags:
- pat:{{ manager_port }}:443
{% else %}
tags: []
{% endif %}
x: -280
y: -80
interfaces:
Expand Down Expand Up @@ -773,7 +778,7 @@ lab:
description: This lab was deployed using SD-WAN lab automation.
notes: |-
-- Do not delete this text --
manager_external_ip = {{ manager_external_ip }}
manager_external_ip = {{ manager_external_ip }}:{{ manager_port }}
-- Do not delete this text --
title: '{{ title }}'
version: 0.2.1
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ write_files:
</vpn-instance>
<vpn-instance>
<vpn-id>512</vpn-id>
{% if not patty_used %}
<ip>
<route>
<prefix>0.0.0.0/0</prefix>
Expand All @@ -96,10 +97,15 @@ write_files:
</next-hop>
</route>
</ip>
{% endif %}
<interface>
<if-name>eth0</if-name>
<ip>
{% if patty_used %}
<dhcp-client>true</dhcp-client>
{% else %}
<address>{{ manager_external_ip }}{{ external_subnet_mask }}</address>
{% endif %}
</ip>
<shutdown>false</shutdown>
</interface>
Expand Down
6 changes: 5 additions & 1 deletion catalyst_sdwan_lab/tasks/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def main(
cml_user: str,
cml_password: str,
manager_ip: str,
manager_port: int,
manager_user: str,
manager_password: str,
lab_name: str,
Expand Down Expand Up @@ -124,7 +125,10 @@ def main(

log.info("Logging in to SD-WAN Manager...")
manager_session = create_manager_session(
url=manager_ip, username=manager_user, password=manager_password
url=manager_ip,
username=manager_user,
password=manager_password,
port=manager_port,
)
manager_config_settings = manager_session.endpoints.configuration_settings
org_name = manager_config_settings.get_organizations()[0].org
Expand Down
15 changes: 10 additions & 5 deletions catalyst_sdwan_lab/tasks/backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from cisco_sdwan.tasks.implementation import BackupArgs, TaskBackup
from jinja2 import Environment, FileSystemLoader
from ruamel.yaml import YAML
from ruamel.yaml.scalarstring import LiteralScalarString
from virl2_client import ClientLibrary
from virl2_client.models.cl_pyats import ClPyats

Expand Down Expand Up @@ -160,6 +161,7 @@ def main(
cml_user: str,
cml_password: str,
manager_ip: str,
manager_port: int,
manager_user: str,
manager_password: str,
lab_name: str,
Expand Down Expand Up @@ -192,7 +194,10 @@ def main(
# Login to SD-WAN Manager
log.info("Logging in to SD-WAN Manager...")
manager_session = create_manager_session(
url=manager_ip, username=manager_user, password=manager_password
url=manager_ip,
username=manager_user,
password=manager_password,
port=manager_port,
)
manager_config_settings = manager_session.endpoints.configuration_settings
org_name = manager_config_settings.get_organizations()[0].org
Expand Down Expand Up @@ -369,9 +374,9 @@ def main(
"cat-sdwan-controller",
"cat-sdwan-edge",
]:
lab_extract["nodes"][i]["configuration"] = custom_node_backup[
lab_extract["nodes"][i]["label"]
]
lab_extract["nodes"][i]["configuration"] = LiteralScalarString(
custom_node_backup[lab_extract["nodes"][i]["label"]]
)

os.mkdir(workdir)
with open(rf"{workdir}/cml_topology.yaml", "w") as file:
Expand All @@ -388,7 +393,7 @@ def main(
)

with Rest(
base_url=f"https://{manager_ip}",
base_url=f"https://{manager_ip}:{manager_port}",
username=manager_user,
password=manager_password,
) as api:
Expand Down
4 changes: 2 additions & 2 deletions catalyst_sdwan_lab/tasks/delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ def main(
# so we ask user to make sure the lab names are unique
if len(lab) > 1:
exit(
f'There are multiple labs/topologies with name "{lab_name}". Please make sure '
f"There are multiple labs/topologies with name '{lab_name}'. Please make sure "
f"lab names are unique and rerun the delete task."
)
lab = lab[0]

if not force:
confirmation = input(
"\nThis will remove lab and all its data. "
f"\nThis will remove '{lab_name}' lab and all its data. "
"Are you sure you want to proceed? (yes/no): "
)
if confirmation.lower() != "yes":
Expand Down
31 changes: 26 additions & 5 deletions catalyst_sdwan_lab/tasks/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def main(
cml: ClientLibrary,
cml_ip: str,
manager_ip: str,
manager_port: int,
manager_mask: str,
manager_gateway: str,
manager_user: str,
Expand All @@ -46,6 +47,7 @@ def main(
lab_name: str,
bridge: str,
dns_server: str,
patty_used: bool,
retry: bool,
loglevel: Union[int, str],
) -> None:
Expand Down Expand Up @@ -78,7 +80,17 @@ def main(
# Prepare the CA for controllers certificate signing
ca_cert, ca_key, ca_chain = load_certificate_details()

if not lab_name:
if lab_name:
# Verify lab name is not duplicated
# Although CML allows labs with same name,
# this crete confusion for other tasks where lab name is used
existing_lab_names = [lab.title for lab in cml.all_labs(show_all=True)]
if lab_name in existing_lab_names:
exit(
f"Lab with name '{lab_name}' already exists. "
f"Please provide a different name to avoid confusion."
)
else:
# User didn't provide lab name, generate by default
# Find existing sdwan labs names
lab_list_search = [
Expand All @@ -102,6 +114,10 @@ def main(
# Encrypt SD-WAN Manager password to SHA512. The encrypted password will be used in bootstrap configuration.
encrypted_manager_password = sha512_crypt.encrypt(manager_password, rounds=5000)

if patty_used:
# PATty should be used for SD-WAN Manager reachability. This means bridge configuration needs to be set to NAT
bridge = "NAT"

cml_topology = cml_tp_tmpl.render(
title=lab_name,
manager_image=manager_image,
Expand All @@ -120,6 +136,8 @@ def main(
external_gateway=manager_gateway,
bridge=bridge,
dns_server=dns_server,
manager_port=manager_port,
patty_used=patty_used,
)

if retry:
Expand All @@ -134,8 +152,10 @@ def main(
)
else:
# Prepare and deploy the lab to CML
# Check if the IP allocated for SD-WAN Manager is not already it use.
check_manager_ip_is_free(manager_ip)

if not patty_used:
# If PATty is not used, check if the IP allocated for SD-WAN Manager is not already it use.
check_manager_ip_is_free(manager_ip)
log.info("Importing the lab...")
lab = cml.import_lab(cml_topology, lab_name)
track_progress(log, "Waiting for nodes to boot...")
Expand All @@ -144,7 +164,7 @@ def main(

# Wait for SD-WAN Manager API to be available
manager_session = wait_for_manager_session(
manager_ip, manager_user, manager_password, log
manager_ip, manager_port, manager_user, manager_password, log
)
# Configure basic settings like org-name, validator fqdn etc.
configure_manager_basic_settings(manager_session, ca_chain, log)
Expand Down Expand Up @@ -175,6 +195,7 @@ def main(

restore_manager_configuration(
manager_ip,
manager_port,
manager_user,
manager_password,
join(MANAGER_CONFIGS_DIR, f"v{config_version}"),
Expand All @@ -191,7 +212,7 @@ def main(
f"#############################################\n"
f"Lab is deployed.\n"
f"CML URL: https://{cml_ip}\n"
f"SD-WAN Manager URL: https://{manager_ip}:8443\n"
f"SD-WAN Manager URL: https://{manager_ip}:{manager_port}\n"
f"Use the username/password set with the script for CML and SD-WAN Manager login.\n"
f"All other nodes use default username/password.\n"
f"#############################################"
Expand Down
Loading

0 comments on commit 90b7f21

Please sign in to comment.