网络自动化运维 Help

4-3 graphviz库绘制拓扑图

完整的实验有几部分组成:

  • eNSP中的节点作为服务,见4-1节内容

  • netmiko库访问S0节点,取得lldp回传信息,见4-2

  • 整理回传信息为后面所需的数据结构,见4-2末尾

  • 节点信息传入Graphviz绘图,本节

逐个击破,本节仅考虑 节点信息传入Graphviz绘图,因此是否恢复4-1服务状态看个人需求,可做可不做。

安装Graphviz

介绍:Graphviz是一个图表绘制工具,包括网络拓扑图,使用dot语言来定义图结构(实验结束后会生成、一看便懂)。
后续大概步骤:安装此软件,安装此软件的python调用接口,最后用python代码调用接口调用此软件。

  1. graphviz官网 下载windows x86-64bit.exe。
    或从本电子文档下载 ⏬安装包graphviz-install-12.2.1-win64.exe

  2. 安装时注意勾选添加环境变量。默认安装路径为 C:\Program Files\Graphviz\bin

    4 2graphviz

  3. 安装后确认
    Windows Powershell终端键入命令dot -h ,由于安装时选择了添加可执行程序到Windows环境变量Path中,因此可以直接调用Graphviz的命令行工具。
    (安装时勾选了桌面快捷方式,但我并没有找到,到安装目录下再找找。)
    这个工具的基本用法是接收dot语言描述的图结构,然后生成图。

    4 2graphviz

  4. 安装此工具的Python接口实现,graphviz库。

    # Windows终端环境下 pip install graphviz
  5. 总结 一般情况下,只需要安装python库,然后就能调用库。而在这个工具中,graphviz库是接口抽象,实际会调用本地安装的graphviz原生程序,这种python代码调用图形化界面工具的例子比较少见。 在这里,python的作用是"胶水"。
    python代码 ➡️ graphviz库 ➡️ 真正的graphviz软件。

简单例子

先根据官网例子尝试核心功能是否能走通。

# 添加临时环境变量Path,让程序能正确找到Graphviz软件。 # 在Windows设置"修改环境变量"窗口中查看环境变量Path。或在Python交互式解释器中查看os.environ["PATH"]的值。 # 即使已经添加环境变量,但代码并没有自动找到程序,经过测试还是添加临时环境变量能成功和简单。 # 添加路径应该与安装路径一致。路径不对或软件未安装将报错报错"make sure the Graphviz executables are on your systems"。 import os import graphviz os.environ["PATH"] += ';' + r'C:\Program Files\Graphviz\bin' # 实例化 dot = graphviz.Digraph('round-table', comment='The Round Table') # 添加节点 dot.node('A', 'King Arthur') dot.node('B', 'Sir Bedevere the Wise') dot.node('L', 'Sir Lancelot the Brave') # 添加边 dot.edges(['AB', 'AL']) dot.edge('B', 'L', constraint='false') # 指定一个存放结果图片的文件夹 dot.render(directory='./outputdir', view=True) # 可在文件夹下看到配置定义源文件和生成的pdf,如果弹窗询问可以用WPS或浏览器打开pdf文件。

在输出文件夹中查看dot语法文件和图片结果

4 2graphviz

由上节课数据生成拓扑

import os import graphviz as gv # 添加环境变量,能正确调用graphviz程序 os.environ["PATH"] += os.pathsep + r'C:\Program Files\Graphviz\bin' # 确保与graphviz安装路径一致 # 拓扑字典,由上节课输出信息得出 topology_dict = { ('S0', 'GE0/0/1'): ('Cloud', 'GE0/0/1'), ('S0', 'GE0/0/2'): ('S1', 'GE0/0/1'), ('S0', 'GE0/0/3'): ('S2', 'GE0/0/1'), } # 获取所有节点 nodes = set( [item[0] for item in list(topology_dict.keys()) + list(topology_dict.values())] ) # 创建 Graphviz 图 graph = gv.Graph(format="svg") # 添加节点 for node in nodes: graph.node(node) # 添加边 for key, value in topology_dict.items(): head, t_label = key tail, h_label = value graph.edge(head, tail, headlabel=h_label, taillabel=t_label, label=" " * 12) # 渲染并保存图像 filename = graph.render(filename="./outputdir/topology") print(f'save in {filename}')

打开topology.svg,可见结果与eNSP中拓扑完全一致。

4 3graphviz 2

意义:从效果上看,最终结果绕了一圈跟eNSP中拓扑图一致。但eNSP毕竟是别人的软件、和软件本身用作学习没有管理功能。代码通过搜集自家企业设备信息,可以建立自己的数据库并进行个性化的管理, 和进行个性化软件开发、例如数据大屏。

(选做)带有定义图标样式参数和方法封装的改进例子

# -*- coding: utf-8 -*- import sys try: import graphviz as gv except ImportError: print("Module graphviz needs to be installed") print("pip install graphviz") sys.exit() # 关于图表绘制UI上的一些配置,如图标名字、字体颜色、线条粗细等。 styles = { "graph": { "label": "拓扑图", "fontsize": "10", "fontcolor": "white", "bgcolor": "#3F3F3F", "rankdir": "BT", }, "nodes": { "fontname": "Helvetica", "shape": "box", "fontcolor": "white", "color": "#006699", "style": "filled", "fillcolor": "#006699", "margin": "0.4绘图", }, "edges": { "style": "dashed", "color": "green", "arrowhead": "open", "fontname": "Courier", "fontsize": "14", "fontcolor": "white", }, } def apply_styles(graph, styles): """ 绘制节点、边""" graph.graph_attr.update(("graph" in styles and styles["graph"]) or {}) graph.node_attr.update(("nodes" in styles and styles["nodes"]) or {}) graph.edge_attr.update(("edges" in styles and styles["edges"]) or {}) return graph def draw_topology(topology_dict, out_filename="topology.svg", style_dict=styles): """ topology_dict - 准备的节点数据结构如下 topology_dict: {('R4', 'Eth0/1'): ('R5', 'Eth0/1'), ('R4', 'Eth0/2'): ('R6', 'Eth0/0')} 边结构: [ R5 ]-Eth0/1 --- Eth0/1-[ R4 ]-Eth0/2---Eth0/0-[ R6 ] 输出拓扑图为topology.svg格式 """ nodes = set( [item[0] for item in list(topology_dict.keys()) + list(topology_dict.values())] ) graph = gv.Graph(format="svg") for node in nodes: graph.node(node) for key, value in topology_dict.items(): head, t_label = key tail, h_label = value graph.edge(head, tail, headlabel=h_label, taillabel=t_label, label=" " * 12) graph = apply_styles(graph, style_dict) filename = graph.render(filename=out_filename) print(f'save in {filename}') if __name__ == '__main__': # 添加环境变量,能正确调用graphviz程序 import os os.environ["PATH"] += os.pathsep + r'C:\Program Files\Graphviz\bin' # 课上eNSP三个节点,通过lldp发现后得到的数据,保存到json本地文件。然后整理成易于绘图库使用的特定格式,下面的字典每一项键值对都是元组,每一个元组的两项为设备名和网口编号。 # ['Local Intf Neighbor Dev Neighbor Intf Exptime', # 'GE0/0/1 - 0a00-2700-0009 3345 ', # 'GE0/0/2 S1 GE0/0/1 109 ', # 'GE0/0/3 S2 GE0/0/1 98 '] topology_dict = { ('S0', 'GE0/0/1'): ('Cloud', 'GE0/0/1'), ('S0', 'GE0/0/2'): ('S1', 'GE0/0/1'), ('S0', 'GE0/0/3'): ('S2', 'GE0/0/1'), } draw_topology(topology_dict)
18 三月 2025