基于HCL的python脚本编程(2)
1. 脚本编程
1.1 手搓部分1:搭建基础拓扑及基本配置

按如图搭建拓扑结构,启动,配置好ip,给各个路由器都配置好telnet(及ssh登录),用户名为各设备的名称,所有登录密码为Admin@12345,这样,就可以从宿主机远程登录各设备。
1.2 手搓部分2:基础配置
如R14配置文件,disp cur:
python
#
version 7.1.064, Release 0427P22
#
sysname R14
#
telnet server enable
#
system-working-mode standard
xbar load-single
password-recovery enable
lpu-type f-series
#
vlan 1
#
interface Serial1/0
#
interface Serial2/0
#
interface Serial3/0
#
interface Serial4/0
#
interface NULL0
#
interface GigabitEthernet0/0
port link-mode route
combo enable copper
ip address 192.168.0.174 255.255.255.0
#
interface GigabitEthernet0/1
port link-mode route
combo enable copper
#
interface GigabitEthernet0/2
port link-mode route
combo enable copper
#
interface GigabitEthernet5/0
port link-mode route
combo enable copper
#
interface GigabitEthernet5/1
port link-mode route
combo enable copper
#
interface GigabitEthernet6/0
port link-mode route
combo enable copper
#
interface GigabitEthernet6/1
port link-mode route
combo enable copper
#
scheduler logfile size 16
#
line class aux
user-role network-operator
#
line class console
user-role network-admin
#
line class tty
user-role network-operator
#
line class vty
user-role network-operator
#
line aux 0
user-role network-operator
#
line con 0
user-role network-admin
#
line vty 0 63
authentication-mode scheme
user-role network-operator
idle-timeout 0 0
#
ssh server enable
ssh user R14 service-type stelnet authentication-type password
#
domain system
#
domain default enable system
#
role name level-0
description Predefined level-0 role
#
role name level-1
description Predefined level-1 role
#
role name level-2
description Predefined level-2 role
#
role name level-3
description Predefined level-3 role
#
role name level-4
description Predefined level-4 role
#
role name level-5
description Predefined level-5 role
#
role name level-6
description Predefined level-6 role
#
role name level-7
description Predefined level-7 role
#
role name level-8
description Predefined level-8 role
#
role name level-9
description Predefined level-9 role
#
role name level-10
description Predefined level-10 role
#
role name level-11
description Predefined level-11 role
#
role name level-12
description Predefined level-12 role
#
role name level-13
description Predefined level-13 role
#
role name level-14
description Predefined level-14 role
#
user-group system
#
local-user R14 class manage
password hash $h$6$cCG2CWWS+FmHQ/zc$g5K50g0rZznQo/Xq93tL/ub2n9EvI3pRoTzdxWqx83pLwm6fsa+rcGKA55EuCdiDa9aiZqaPcOUpmgcrexu/ww==
service-type ssh telnet
authorization-attribute user-role network-admin
authorization-attribute user-role network-operator
#
将上述内容拷贝下来,按照其他设备的名称和ip修改:
python
#
version 7.1.064, Release 0427P22
#
sysname R13
#
......
ip address 192.168.0.173 255.255.255.0
#
......
#
ssh server enable
ssh user R13 service-type stelnet authentication-type password
#
domain system
......
#
user-group system
#
local-user R13 class manage
......
#
进入R13,进入系统视图,将以上修改后的内容,贴进去,保存即可。
1.3 在vscode里求助workbuddy,创建目录结构

将交换机路由器常见的、常用的功能,写成一个工具集,
python
# -*- coding: utf-8 -*-
"""
文件名 :hclnet.py
功能 :HCL 自动化公共库(登录 / 下发 / 保存 / 采集 / 结果汇总)
说明 :
1. 所有案例脚本都 import 这个文件,不用每个脚本重复写 ConnectHandler。
2. 登录会自动尝试多种认证方式:devices.yaml 里的账号 -> none 认证 -> admin。
HCL 里有的设备开了 scheme 认证,有的还是 telnet none 认证,这个兜底很好使。
3. 下发后自动扫描回显里的 "%"、"Wrong parameter" 等字样,
避免出现"脚本说成功、其实命令没配进去"的假成功。
4. HCL 是模拟器,比真机慢,所以默认 fast_cli=False + 更长 read_timeout。
作者 :wuzz@cqie
时间 :
"""
from __future__ import annotations
import argparse
import logging
import re
import time
from pathlib import Path
import yaml
from netmiko import ConnectHandler
BASE_DIR = Path(__file__).resolve().parent.parent # 即hcl-lab/
DEVICES_FILE = BASE_DIR / "devices.yaml"
OUTPUT_DIR = BASE_DIR / "output"
DEFAULT_PASSWORD = "Admin@12345"
log = logging.getLogger("hcl")
# 接口简写 -> 全称(display interface brief 里显示的是简写)
INTF_PREFIX = {
"XGE": "Ten-GigabitEthernet",
"FGE": "FortyGigE",
"M-GE": "M-GigabitEthernet",
"GE": "GigabitEthernet",
"BAGG": "Bridge-Aggregation",
"RAGG": "Route-Aggregation",
"Vlan": "Vlan-interface",
"Loop": "LoopBack",
"Tun": "Tunnel",
"Ser": "Serial",
"NULL": "NULL",
}
# 这些字样出现在回显里,说明命令没被设备接受
ERR_PATTERNS = (
"% ",
"%^",
"Wrong parameter",
"Unrecognized command",
"Incomplete command",
"Too many parameters",
"Error:",
"Failed to",
)
_ABBR_GROUP = "|".join(sorted(INTF_PREFIX, key=len, reverse=True))
_IF_ROW = re.compile(
rf"^(?P<short>(?:{_ABBR_GROUP})[\w/.:-]*)\s+(?P<link>UP|DOWN|ADM)\s+(?P<rest>.*)$"
)
# --------------------------------------------------------------------------
# 日志 / 参数
# --------------------------------------------------------------------------
def setup_logging(debug: bool = False) -> None:
logging.basicConfig(
level=logging.DEBUG if debug else logging.INFO,
format="%(asctime)s %(levelname)-7s %(message)s",
datefmt="%H:%M:%S",
)
logging.getLogger("netmiko").setLevel(logging.WARNING)
logging.getLogger("paramiko").setLevel(logging.WARNING)
def build_parser(description: str) -> argparse.ArgumentParser:
"""每个案例脚本统一用这套命令行参数。"""
p = argparse.ArgumentParser(
description=description, formatter_class=argparse.RawDescriptionHelpFormatter
)
p.add_argument("--dry-run", action="store_true", help="只打印要下发的命令,不连设备")
p.add_argument("-y", "--yes", action="store_true", help="跳过交互确认")
p.add_argument("--only", help="只操作部分设备,逗号分隔,如 R1,R2")
p.add_argument("--no-save", action="store_true", help="下发后不执行 save force")
p.add_argument("--debug", action="store_true", help="输出调试日志")
return p
# --------------------------------------------------------------------------
# 设备清单
# --------------------------------------------------------------------------
def load_devices(only: str | None = None, path: Path | str = DEVICES_FILE) -> list[dict]:
data = yaml.safe_load(Path(path).read_text(encoding="utf-8")) or {}
devs = []
for d in data.get("devices") or []:
if not d.get("enabled", True):
continue
d.setdefault("device_type", "hp_comware_telnet")
d.setdefault("port", 23)
d.setdefault("username", "")
d.setdefault("password", "")
devs.append(d)
if only:
want = {x.strip().upper() for x in only.split(",") if x.strip()}
devs = [d for d in devs if str(d["name"]).upper() in want]
return devs
def full_name(short: str) -> str:
"""GE0/0 -> GigabitEthernet0/0"""
for abbr in sorted(INTF_PREFIX, key=len, reverse=True):
if short.lower().startswith(abbr.lower()):
return INTF_PREFIX[abbr] + short[len(abbr):]
return short
def short_name(name: str) -> str:
"""GigabitEthernet0/0 -> GE0/0"""
for full in sorted(INTF_PREFIX.values(), key=len, reverse=True):
if name.lower().startswith(full.lower()):
return _abbr_of(full) + name[len(full):]
return name
def _abbr_of(full: str) -> str:
for abbr, value in INTF_PREFIX.items():
if value == full:
return abbr
return full
# --------------------------------------------------------------------------
# 连接 / 下发 / 保存 / 采集
# --------------------------------------------------------------------------
def _auth_candidates(dev: dict) -> list[tuple[str, str]]:
"""可能的认证方式,按命中概率排序:
1) devices.yaml 里写的账号
2) 设备名当用户名(本项目约定 local-user <主机名> / Admin@12345)
3) 空账号(HCL 出厂默认 telnet none 认证)
4) admin
"""
cands = []
if dev.get("username"):
cands.append((dev["username"], dev.get("password") or ""))
cands.append((str(dev["name"]), dev.get("password") or DEFAULT_PASSWORD))
cands.append(("", "")) # HCL 默认 telnet none
cands.append(("admin", dev.get("password") or DEFAULT_PASSWORD))
seen, uniq = set(), []
for c in cands:
if c not in seen:
seen.add(c)
uniq.append(c)
return uniq
def connect(dev: dict, session_log: bool = False):
"""登录一台设备,返回 netmiko 连接对象。失败抛 RuntimeError。"""
name = dev["name"]
errors = []
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
for user, pwd in _auth_candidates(dev):
params = {
"device_type": dev.get("device_type", "hp_comware_telnet"),
"host": dev["host"],
"port": int(dev.get("port", 23)),
"username": user,
"password": pwd,
"timeout": 15,
"conn_timeout": 15,
"banner_timeout": 30,
"auth_timeout": 30,
"fast_cli": False, # HCL 卡,开 fast_cli 容易读不全回显
"global_delay_factor": 1,
}
if session_log:
params["session_log"] = str(OUTPUT_DIR / f"session_{name}.log")
t0 = time.time()
try:
conn = ConnectHandler(**params)
except Exception as exc: # noqa: BLE001
errors.append(f"{user or '<none>'}: {type(exc).__name__}")
continue
log.info("[%s] 登录成功(%s,%.1fs)", name, user or "none 认证", time.time() - t0)
return conn
raise RuntimeError(
f"{name}({dev['host']}) 登录失败:{'; '.join(errors)} | "
"排查:ping 通吗 / telnet server enable / VTY 认证方式 / 账号密码"
)
def find_errors(text: str) -> list[str]:
bad = []
for line in (text or "").splitlines():
s = line.strip()
if s and any(p in s for p in ERR_PATTERNS):
bad.append(s[:120])
return bad
def push_config(conn, commands: list[str], name: str = "", read_timeout: int = 120) -> str:
if not commands:
return ""
log.info("[%s] 进入 system-view,下发 %d 条命令", name, len(commands))
out = conn.send_config_set(commands, cmd_verify=False, read_timeout=read_timeout)
problems = find_errors(out)
if problems:
log.warning("[%s] 回显中发现 %d 处异常", name, len(problems))
for p in problems:
log.warning(" > %s", p)
return out
def save_config(conn, name: str = "") -> bool:
"""Comware 用 save force,不会弹 Y/N,一次搞定。"""
try:
out = conn.send_command_timing("save force", read_timeout=180)
except Exception as exc: # noqa: BLE001
log.error("[%s] 保存失败:%s", name, exc)
return False
ok = "successfully" in out.lower()
log.info("[%s] 保存配置 %s", name, "[OK]" if ok else "[WARN] 请手工确认")
return ok
def show(conn, command: str, read_timeout: int = 120) -> str:
return conn.send_command(command, read_timeout=read_timeout)
def get_interfaces(conn) -> list[dict]:
"""解析 display interface brief,返回 [{short, full, link, protocol, ip}]"""
out = show(conn, "display interface brief")
result = []
for line in out.splitlines():
m = _IF_ROW.match(line.strip())
if not m:
continue
rest = m.group("rest").split()
protocol, ip = "-", ""
if rest:
if rest[0] in ("UP", "DOWN"):
protocol = rest[0]
rest = rest[1:]
else:
protocol = "UP" if m.group("link") == "UP" else "-"
if rest and re.match(r"^\d+\.\d+\.\d+\.\d+$", rest[0]):
ip = rest[0]
result.append(
{
"short": m.group("short"),
"full": full_name(m.group("short")),
"link": m.group("link"),
"protocol": protocol,
"ip": ip,
}
)
return result
def check_interfaces(conn, dev_name: str, wanted: list[str]) -> list[str]:
"""预检:设备上是否真的存在这些接口(拼错端口名时能立刻发现)。"""
have = {i["short"].lower() for i in get_interfaces(conn)}
if not have:
return []
missing = [w for w in wanted if short_name(w).lower() not in have]
if not missing:
return []
sample = ", ".join(sorted(have)[:12])
return [f"设备上找不到接口 {', '.join(missing)};实际有:{sample} ...; 请改脚本顶部的接口名"]
# --------------------------------------------------------------------------
# 统一的"打印计划 -> 确认 -> 批量执行 -> 汇总"流程
# --------------------------------------------------------------------------
def run_plan(
title: str,
plan: dict[str, list[str]],
devices: list[dict],
save: bool = True,
dry_run: bool = False,
assume_yes: bool = False,
verify: tuple[str, ...] = (),
precheck=None,
) -> list[dict]:
plan = {k: v for k, v in plan.items() if v}
dev_map = {d["name"]: d for d in devices}
skipped = [k for k in plan if k not in dev_map]
targets = [dev_map[k] for k in plan if k in dev_map]
if skipped:
log.debug("以下设备在计划里但未加载(被 --only 过滤或 enabled:false):%s", ", ".join(skipped))
print("\n" + "=" * 74)
print(title)
print(f"目标设备 {len(targets)} 台:{', '.join(d['name'] for d in targets) or '无'}")
print("=" * 74)
for d in targets:
print(f"\n--- {d['name']} {d['host']}:{d.get('port', 23)} ---")
for c in plan[d["name"]]:
print(" " + c)
if not targets:
print("\n没有要执行的设备。")
return []
if dry_run:
print(f"\n[dry-run] 以上是 {len(targets)} 台设备要下发的命令,未连接设备。")
return []
if not assume_yes:
ans = input(f"\n确认把以上配置下发到 {len(targets)} 台设备?输入 yes 继续:").strip().lower()
if ans not in ("yes", "y"):
print("已取消。")
return []
results = []
for d in targets:
name = d["name"]
rec = {"name": name, "ok": False, "problems": [], "error": "", "saved": None, "verify": {}}
t0 = time.time()
try:
conn = connect(d)
try:
blocked = precheck(conn, d) if precheck else []
if blocked:
rec["problems"] = blocked
log.error("[%s] 预检未通过,跳过下发", name)
else:
out = push_config(conn, plan[name], name)
rec["problems"] = find_errors(out)
for cmd in verify:
rec["verify"][cmd] = show(conn, cmd)
if save and not rec["problems"]:
rec["saved"] = save_config(conn, name)
rec["ok"] = not rec["problems"]
finally:
conn.disconnect()
except Exception as exc: # noqa: BLE001
rec["error"] = f"{type(exc).__name__}: {exc}"
log.error("[%s] 执行失败:%s", name, rec["error"])
rec["elapsed"] = time.time() - t0
results.append(rec)
print_report(results, verify)
return results
def print_report(results: list[dict], verify: tuple[str, ...] = ()) -> None:
print("\n" + "=" * 74)
print("执行结果")
print("=" * 74)
width = max([len(r["name"]) for r in results] + [6])
ok_cnt = 0
for r in results:
if r.get("error"):
status = "[FAIL] " + r["error"]
elif r["problems"]:
status = f"[WARN] 回显异常 {len(r['problems'])} 处"
else:
ok_cnt += 1
status = "[OK]"
if r.get("saved"):
status += " 已保存"
elif r.get("saved") is False:
status += " 保存失败,请手工 save"
print(f"{r['name']:<{width}} {status} ({r.get('elapsed', 0):.1f}s)")
for p in r["problems"][:6]:
print(" > " + p)
print(f"\n合计 {len(results)} 台,成功 {ok_cnt} 台。")
for r in results:
for cmd, out in (r.get("verify") or {}).items():
print(f"\n--- {r['name']} : {cmd} ---")
print((out or "").strip())
有了工具,马上安排第一个HCL脚本:
python
"""
FilePath: /h3hcl/hcl-lab/scripts/01_telnet_test.py
Author: wuzz@cqie
Date: 2026-10-24 08:17:40
LastEditTime: 2026-10-24 17:47:28
LastEditors: Do not edit
Version: 1.0.0
Description:
License: CQIE
"""
# -*- coding: utf-8 -*-
"""第一个脚本:Telnet 连接 HCL 设备,采集版本信息。
运行前提:HCL 拓扑已搭好、设备配好 IP 和 Telnet、宿主机能 ping 通设备。
"""
from netmiko import ConnectHandler
dev = {
"device_type": "hp_comware_telnet",
"host": "192.168.0.175",
"port": 23,
"username": "R15",
"password": "Admin@12345",
"fast_cli": True,
"session_log": "step1.log"
}
def main():
try:
with ConnectHandler(**dev) as conn:
conn.send_command("screen-length disable")
output = conn.send_command("display version")
print(output)
except Exception as e:
print(f"连接失败:{e}")
print("排查顺序:1) ping 通吗 2) telnet server enable 了吗 3) VTY 配置对吗")
if __name__ == "__main__":
main()
运行结果:
powershell
(venv) PS F:\workbuddywork\h3hcl\hcl-lab\scripts> python .\01_telnet_test.py
F:\workbuddywork\h3hcl\hcl-lab\scripts\01_telnet_test.py:2: SyntaxWarning: "\h" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\h"? A raw string is also an option.
FilePath: \h3hcl\hcl-lab\scripts\01_telnet_test.py
H3C Comware Software, Version 7.1.064, Release 0427P22
Copyright (c) 2004-2021 New H3C Technologies Co., Ltd. All rights reserved.
H3C MSR36-20 uptime is 0 weeks, 0 days, 5 hours, 41 minutes
Last reboot reason: User reboot
Boot image: flash:/msr36-cmw710-boot-r0424p22.bin
Boot image version: 7.1.064, Release 0427P22
Compiled Mar 16 2021 15:00:00
Boot image: flash:/msr36-cmw710-system-r0424p22.bin
Boot image version: 7.1.064, Release 0427P22
Compiled Mar 16 2021 15:00:00
CPU ID: 0x2
384M bytes DDR3 SDRAM Memory
1024M bytes Flash Memory
PCB Version: 2.0
CPLD Version: 1.0
Basic BootWare Version: 1.42
Extended BootWare Version: 1.42
查看各设备的接口等情况,为后边的连接提供基础数据:
powershell
# -*- coding: utf-8 -*-
# @FilePath: \h3hcl\hcl-lab\scripts\02_discover.py
# @Author: wuzz@cqie
# @Date: 2026-10-24 08:17:40
# @LastEditTime: 2026-10-24 17:47:28
# @LastEditors: wuzz@cqie
# @Version: 1.0.0
# @Description:
# @License: CQIE
"""
文件名 :02_discover.py
功能 :巡检摸底 ------ 登录 devices.yaml 里所有设备,采集版本 + 接口清单
用法 :
python scripts\\02_discover.py # 全部设备
python scripts\\02_discover.py --only R1,R2 # 只摸两台
产出 :
output/discover_<时间>/<设备名>.txt 原始回显
output/inventory.json 每台设备的接口摘要(给后面的脚本对照用)
为什么要先跑这个:
自动配置最容易翻车的地方就是接口名写错(GE0/0 还是 GE0/1?)
跑一遍就能看清每台机器到底有几个口、哪个口 up、现在什么 IP。
"""
from __future__ import annotations
import json
from datetime import datetime
from hclnet import (
OUTPUT_DIR,
build_parser,
connect,
get_interfaces,
load_devices,
setup_logging,
show,
)
CMDS = {
"version": "display version",
"ip_brief": "display ip interface brief",
"interface_brief": "display interface brief",
}
def main() -> None:
args = build_parser(__doc__).parse_args()
setup_logging(args.debug)
devices = load_devices(args.only)
if not devices:
print("devices.yaml 里没有匹配的设备。")
return
stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
out_dir = OUTPUT_DIR / f"discover_{stamp}"
out_dir.mkdir(parents=True, exist_ok=True)
inventory = {}
for dev in devices:
name = dev["name"]
print("\n" + "=" * 74)
print(f"{name} ({dev['host']})")
print("=" * 74)
try:
conn = connect(dev)
except Exception as exc: # noqa: BLE001
print(f"[FAIL] {exc}")
inventory[name] = {"host": dev["host"], "error": str(exc)}
continue
try:
prompt = conn.find_prompt()
raw = {k: show(conn, c) for k, c in CMDS.items()}
intfs = get_interfaces(conn)
finally:
conn.disconnect()
# 原始回显落盘,方便事后对照
(out_dir / f"{name}.txt").write_text(
f"host: {dev['host']}\nprompt: {prompt}\n\n"
+ "\n\n".join(f"##### {k}\n{v}" for k, v in raw.items()),
encoding="utf-8",
)
ips = [i["ip"] for i in intfs if i["ip"]]
print(f"提示符 {prompt} 接口 {len(intfs)} 个 已配 IP {len(ips)} 个")
for i in intfs:
flag = "up " if i["link"] == "UP" else "down"
print(f" {i['short']:<12} {flag} {i['ip'] or '-'}")
inventory[name] = {
"host": dev["host"],
"prompt": prompt,
"interfaces": intfs,
}
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
inv_file = OUTPUT_DIR / "inventory.json"
inv_file.write_text(json.dumps(inventory, indent=2, ensure_ascii=False), encoding="utf-8")
print("\n" + "=" * 74)
print(f"采集完成:{len(inventory)} 台")
print(f"原始回显:{out_dir}")
print(f"接口清单:{inv_file}")
print("下一步:打开 output/inventory.json 确认接口名,再改 03_ip_basic.py 顶部的 INTERFACES")
if __name__ == "__main__":
main()
运行结果:
powershell
###运行
==========================================================================
R1 (192.168.0.161)
==========================================================================
18:27:34 INFO [R1] 登录成功(R1,3.3s)
提示符 <R1> 接口 12 个 已配 IP 2 个
GE0/0 up 192.168.0.161
GE0/1 down -
GE0/2 down -
GE5/0 down -
GE5/1 down -
GE6/0 down -
GE6/1 down -
NULL0 up -
Ser1/0 up 12.12.12.1
Ser2/0 down -
Ser3/0 down -
Ser4/0 down -
==========================================================================
R2 (192.168.0.162)
==========================================================================
18:27:41 INFO [R2] 登录成功(R2,3.4s)
提示符 <R2> 接口 12 个 已配 IP 2 个
GE0/0 up 192.168.0.162
GE0/1 down -
GE0/2 down -
GE5/0 down -
GE5/1 down -
GE6/0 down -
GE6/1 down -
NULL0 up -
Ser1/0 up 12.12.12.2
Ser2/0 up -
Ser3/0 down -
Ser4/0 down -
==========================================================================
R3 (192.168.0.163)
==========================================================================
18:27:47 INFO [R3] 登录成功(R3,3.4s)
提示符 <R3> 接口 12 个 已配 IP 1 个
GE0/0 up 192.168.0.163
GE0/1 down -
GE0/2 down -
GE5/0 down -
GE5/1 down -
GE6/0 down -
GE6/1 down -
NULL0 up -
Ser1/0 down -
Ser2/0 up -
Ser3/0 down -
Ser4/0 down -
==========================================================================
R4 (192.168.0.164)
==========================================================================
18:27:54 INFO [R4] 登录成功(R4,3.3s)
提示符 <R4> 接口 12 个 已配 IP 1 个
GE0/0 up 192.168.0.164
GE0/1 down -
GE0/2 down -
GE5/0 down -
GE5/1 down -
GE6/0 down -
GE6/1 down -
NULL0 up -
Ser1/0 down -
Ser2/0 down -
Ser3/0 down -
Ser4/0 down -
==========================================================================
R5 (192.168.100.166)
==========================================================================
18:28:00 INFO [R5] 登录成功(R5,3.3s)
提示符 <R5> 接口 12 个 已配 IP 1 个
GE0/0 up 192.168.100.166
GE0/1 down -
GE0/2 down -
GE5/0 down -
GE5/1 down -
GE6/0 down -
GE6/1 down -
NULL0 up -
Ser1/0 down -
Ser2/0 down -
Ser3/0 down -
Ser4/0 down -
==========================================================================
R11 (192.168.0.171)
==========================================================================
18:28:06 INFO [R11] 登录成功(R11,3.3s)
提示符 <R11> 接口 12 个 已配 IP 1 个
GE0/0 up 192.168.0.171
GE0/1 down -
GE0/2 down -
GE5/0 down -
GE5/1 down -
GE6/0 down -
GE6/1 down -
NULL0 up -
Ser1/0 down -
Ser2/0 down -
Ser3/0 down -
Ser4/0 down -
==========================================================================
R12 (192.168.0.172)
==========================================================================
18:28:13 INFO [R12] 登录成功(R12,3.4s)
提示符 <R12> 接口 12 个 已配 IP 1 个
GE0/0 up 192.168.0.172
GE0/1 down -
GE0/2 down -
GE5/0 down -
GE5/1 down -
GE6/0 down -
GE6/1 down -
NULL0 up -
Ser1/0 down -
Ser2/0 down -
Ser3/0 down -
Ser4/0 down -
==========================================================================
R13 (192.168.0.173)
==========================================================================
18:28:19 INFO [R13] 登录成功(R13,3.3s)
提示符 <R13> 接口 12 个 已配 IP 1 个
GE0/0 up 192.168.0.173
GE0/1 down -
GE0/2 down -
GE5/0 down -
GE5/1 down -
GE6/0 down -
GE6/1 down -
NULL0 up -
Ser1/0 down -
Ser2/0 down -
Ser3/0 down -
Ser4/0 down -
==========================================================================
R14 (192.168.0.174)
==========================================================================
18:28:26 INFO [R14] 登录成功(R14,3.4s)
提示符 <R14> 接口 12 个 已配 IP 1 个
GE0/0 up 192.168.0.174
GE0/1 down -
GE0/2 down -
GE5/0 down -
GE5/1 down -
GE6/0 down -
GE6/1 down -
NULL0 up -
Ser1/0 down -
Ser2/0 down -
Ser3/0 down -
Ser4/0 down -
==========================================================================
R15 (192.168.0.175)
==========================================================================
18:28:32 INFO [R15] 登录成功(R15,3.3s)
提示符 <R15> 接口 12 个 已配 IP 1 个
GE0/0 up 192.168.0.175
GE0/1 down -
GE0/2 down -
GE5/0 down -
GE5/1 down -
GE6/0 down -
GE6/1 down -
NULL0 up -
Ser1/0 down -
Ser2/0 down -
Ser3/0 down -
Ser4/0 down -
==========================================================================
SW1 (192.168.0.201)
==========================================================================
[FAIL] SW1(192.168.0.201) 登录失败:SW1: NetmikoAuthenticationException; <none>: NetmikoAuthenticationException; admin: NetmikoAuthenticationException | 排查:ping 通吗 / telnet server enable / VTY 认证方式 / 账号密码
==========================================================================
采集完成:11 台
原始回显:F:\workbuddywork\h3hcl\hcl-lab\output\discover_20261024_182731
接口清单:F:\workbuddywork\h3hcl\hcl-lab\output\inventory.json
下一步:打开 output/inventory.json 确认接口名,再改 03_ip_basic.py 顶部的 INTERFACES
02_discover.py(摸清接口名)
↓
03_ip_basic.py(批量配 IP / 描述 / 环回口) ← 就是它
↓
04_ospf.py / 05_vlan_gw.py(跑路由、配 VLAN)
down -
GE6/1 down -
NULL0 up -
Ser1/0 down -
Ser2/0 down -
Ser3/0 down -
Ser4/0 down -
==========================================================================
SW1 (192.168.0.201)
FAIL SW1(192.168.0.201) 登录失败:SW1: NetmikoAuthenticationException; : NetmikoAuthenticationException; admin: NetmikoAuthenticationException | 排查:ping 通吗 / telnet server enable / VTY 认证方式 / 账号密码
==========================================================================
采集完成:11 台
原始回显:F:\workbuddywork\h3hcl\hcl-lab\output\discover_20261024_182731
接口清单:F:\workbuddywork\h3hcl\hcl-lab\output\inventory.json
下一步:打开 output/inventory.json 确认接口名,再改 03_ip_basic.py 顶部的 INTERFACES
#
02_discover.py(摸清接口名)
↓
03_ip_basic.py(批量配 IP / 描述 / 环回口) ← 就是它
↓
04_ospf.py / 05_vlan_gw.py(跑路由、配 VLAN)