CMLで「リンク一覧表示」「LinkDown/Up」するPythonスクリプト
以前にCMLでリンクの遅延などを一覧表示するPythonスクリプトというメモを書きました。リンクの一覧表示に加え、「指定したリンクのLinkDown/Up」も実行できるように修正したスクリプトをメモしておきます。CMLへのアクセス情報はハードコードしています。
検証環境¶
| 対象 | バージョン |
|---|---|
| Cisco CML | 2.10.0+build.13 |
サンプルスクリプト¶
import argparse
import logging
from rich import box
from rich.console import Console
from rich.table import Table
from virl2_client import ClientLibrary
URL = "https://10.0.0.1"
USER = "admin"
PASS = "pass"
def parse_args():
parser = argparse.ArgumentParser(
description="Display CML links or bring selected links up/down."
)
parser.add_argument(
"-w",
"--workspace",
help="Display links only in this CML workspace (Workspace ID).",
)
parser.add_argument(
"--link-id",
nargs="+",
metavar="LINK_ID",
help="One or more Link IDs to operate on.",
)
action = parser.add_mutually_exclusive_group()
action.add_argument(
"--down", action="store_true", help="Bring selected links down."
)
action.add_argument("--up", action="store_true", help="Bring selected links up.")
args = parser.parse_args()
if (args.down or args.up) and not args.link_id:
parser.error("--link-id is required with --down or --up")
if args.link_id and not (args.down or args.up):
parser.error("--link-id can only be used with --down or --up")
return args
def print_links(labs, workspace=None):
"""Print links, optionally limited to one workspace."""
title = "CML All Links" if workspace is None else f"CML Links: {workspace}"
table = Table(title=title, box=box.MINIMAL_DOUBLE_HEAD)
for column in (
"Lab",
"Link ID",
"Node-A",
"Node-B",
"State",
"Bandwidth",
"Latency",
"Loss",
"Jitter",
):
table.add_column(column)
links = [
(lab, link)
for lab in labs
if workspace is None or lab.id == workspace
for link in lab.links()
]
links.sort(
key=lambda item: (
f"{item[1].node_a.label} {item[1].interface_a.label}".casefold(),
f"{item[1].node_b.label} {item[1].interface_b.label}".casefold(),
item[0].title.casefold(),
item[1].id.casefold(),
)
)
synced_labs = set()
for lab, link in links:
if lab.id not in synced_labs:
lab.sync_states()
synced_labs.add(lab.id)
is_up = str(link.state).upper() == "STARTED"
link_state = "Up" if is_up else "-"
condition = link.get_condition()
values = [
str(condition.get("bandwidth", "")),
str(condition.get("latency", "")),
str(condition.get("loss", "")),
str(condition.get("jitter", "")),
]
table.add_row(
lab.title,
link.id,
f"{link.node_a.label} {link.interface_a.label}",
f"{link.node_b.label} {link.interface_b.label}",
link_state,
*values,
style="white" if is_up else "grey50",
)
Console().print(table)
def change_links(labs, link_ids, action, workspace=None):
"""Start or stop configured links in the selected workspace."""
links_by_id = {
link.id: link
for lab in labs
if workspace is None or lab.id == workspace
for link in lab.links()
}
missing = [link_id for link_id in link_ids if link_id not in links_by_id]
if missing:
scope = f" in workspace {workspace!r}" if workspace else ""
raise SystemExit("Link IDs not found" + scope + ": " + ", ".join(missing))
for link_id in link_ids:
if action == "down":
links_by_id[link_id].stop()
else:
links_by_id[link_id].start()
def main():
args = parse_args()
logging.getLogger("virl2_client.virl2_client").addFilter(
_SuppressSSLVerificationWarning()
)
cml = ClientLibrary(URL, USER, PASS, ssl_verify=False)
cml.is_system_ready(wait=True)
labs = cml.all_labs()
action = "down" if args.down else "up" if args.up else None
if action is not None:
change_links(labs, args.link_id, action, args.workspace)
labs = cml.all_labs()
print_links(labs, args.workspace)
class _SuppressSSLVerificationWarning(logging.Filter):
"""Suppress only the warning emitted for the intentionally disabled SSL check."""
def filter(self, record):
return record.getMessage() != "SSL Verification disabled"
if __name__ == "__main__":
main()