prod
This commit is contained in:
+197
-15
@@ -37,16 +37,27 @@ func (p *H3CParser) Parse(device *models.Device, outputs []string) error {
|
||||
// outputs[2] 是 display interface
|
||||
fmt.Printf("[H3C DEBUG] display interface output length: %d\n", len(outputs[2]))
|
||||
if len(outputs[2]) > 0 {
|
||||
// 如果输出小于1000字节,完整输出以便调试
|
||||
if len(outputs[2]) <= 1000 {
|
||||
fmt.Printf("[H3C DEBUG] Complete output:\n%s\n", outputs[2])
|
||||
if len(outputs[2]) <= 3000 {
|
||||
fmt.Printf("[H3C DEBUG] Complete interface output:\n%s\n", outputs[2])
|
||||
} else {
|
||||
fmt.Printf("[H3C DEBUG] First 200 chars: %q\n", outputs[2][:200])
|
||||
fmt.Printf("[H3C DEBUG] First 3000 chars:\n%s\n", outputs[2][:3000])
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("[H3C DEBUG] display interface output is EMPTY!\n")
|
||||
}
|
||||
|
||||
// outputs[3] 是 display interface brief
|
||||
fmt.Printf("[H3C BRIEF DEBUG] display interface brief output length: %d\n", len(outputs[3]))
|
||||
if len(outputs[3]) > 0 {
|
||||
if len(outputs[3]) <= 3000 {
|
||||
fmt.Printf("[H3C BRIEF DEBUG] Complete brief output:\n%s\n", outputs[3])
|
||||
} else {
|
||||
fmt.Printf("[H3C BRIEF DEBUG] First 3000 chars:\n%s\n", outputs[3][:3000])
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("[H3C BRIEF DEBUG] display interface brief output is EMPTY!\n")
|
||||
}
|
||||
|
||||
if outputs[2] == "" {
|
||||
fmt.Printf("Warning: 'display interface' output is empty for device %s\n", device.IP)
|
||||
} else {
|
||||
@@ -111,9 +122,9 @@ func (p *H3CParser) parseVersion(device *models.Device, output string) {
|
||||
}
|
||||
}
|
||||
|
||||
uptimeRegex := regexp.MustCompile(`uptime is\s+(\d+\s+\S+)`)
|
||||
uptimeRegex := regexp.MustCompile(`uptime is\s+(.+)`)
|
||||
if matches := uptimeRegex.FindStringSubmatch(output); len(matches) > 1 {
|
||||
device.Uptime = matches[1]
|
||||
device.Uptime = strings.TrimSpace(matches[1])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +153,15 @@ func (p *H3CParser) parseInterfaces(interfaceOutput, briefOutput string) []model
|
||||
|
||||
if brief, ok := briefMap[currentInterface.Name]; ok {
|
||||
currentInterface.IP = brief.IP
|
||||
if currentInterface.Speed == "" {
|
||||
currentInterface.Speed = brief.Speed
|
||||
}
|
||||
if currentInterface.Duplex == "" {
|
||||
currentInterface.Duplex = brief.Duplex
|
||||
}
|
||||
if currentInterface.VLAN == "" {
|
||||
currentInterface.VLAN = brief.VLAN
|
||||
}
|
||||
}
|
||||
pendingInterfaceName = ""
|
||||
continue
|
||||
@@ -162,6 +182,15 @@ func (p *H3CParser) parseInterfaces(interfaceOutput, briefOutput string) []model
|
||||
|
||||
if brief, ok := briefMap[currentInterface.Name]; ok {
|
||||
currentInterface.IP = brief.IP
|
||||
if currentInterface.Speed == "" {
|
||||
currentInterface.Speed = brief.Speed
|
||||
}
|
||||
if currentInterface.Duplex == "" {
|
||||
currentInterface.Duplex = brief.Duplex
|
||||
}
|
||||
if currentInterface.VLAN == "" {
|
||||
currentInterface.VLAN = brief.VLAN
|
||||
}
|
||||
}
|
||||
pendingInterfaceName = ""
|
||||
continue
|
||||
@@ -192,9 +221,48 @@ func (p *H3CParser) parseInterfaces(interfaceOutput, briefOutput string) []model
|
||||
currentInterface.Mask = cidrToMask(matches[2])
|
||||
}
|
||||
|
||||
if speedRegex := regexp.MustCompile(`(\d+)\s+(Kbps|Mbps|Gbps)`); speedRegex.MatchString(line) {
|
||||
// 匹配速度格式1: Speed : 1000Mbps 或 Speed : 10Gbps
|
||||
if speedRegex := regexp.MustCompile(`Speed\s*:\s*(\d+)\s*(Kbps|Mbps|Gbps)`); speedRegex.MatchString(line) {
|
||||
matches := speedRegex.FindStringSubmatch(line)
|
||||
currentInterface.Speed = matches[1] + " " + matches[2]
|
||||
currentInterface.Speed = matches[1] + matches[2]
|
||||
}
|
||||
// 匹配速度格式2: Speed: 1000, Loopback: not set (纯数字Mbps)
|
||||
if speedRegex2 := regexp.MustCompile(`Speed\s*:\s*(\d+)(?:,|$)`); speedRegex2.MatchString(line) && currentInterface.Speed == "" {
|
||||
matches := speedRegex2.FindStringSubmatch(line)
|
||||
currentInterface.Speed = matches[1] + "Mbps"
|
||||
}
|
||||
|
||||
// 匹配双工: Duplex: Full 或 Duplex: Half
|
||||
if duplexRegex := regexp.MustCompile(`Duplex\s*:\s*(\S+)`); duplexRegex.MatchString(line) && currentInterface.Duplex == "" {
|
||||
currentInterface.Duplex = duplexRegex.FindStringSubmatch(line)[1]
|
||||
}
|
||||
|
||||
// 匹配VLAN信息: Port link-type: Access / Trunk / Hybrid
|
||||
if linkTypeRegex := regexp.MustCompile(`Port link-type\s*:\s*(\S+)`); linkTypeRegex.MatchString(line) {
|
||||
linkType := linkTypeRegex.FindStringSubmatch(line)[1]
|
||||
currentInterface.VLAN = linkType
|
||||
}
|
||||
// 匹配Untagged VLAN: Untagged VLAN ID: 10
|
||||
if untaggedRegex := regexp.MustCompile(`Untagged VLAN ID\s*:\s*(\S+)`); untaggedRegex.MatchString(line) {
|
||||
untagged := untaggedRegex.FindStringSubmatch(line)[1]
|
||||
if untagged != "none" && untagged != "--" {
|
||||
if currentInterface.VLAN != "" {
|
||||
currentInterface.VLAN = currentInterface.VLAN + " " + untagged
|
||||
} else {
|
||||
currentInterface.VLAN = untagged
|
||||
}
|
||||
}
|
||||
}
|
||||
// 匹配Tagged VLAN: Tagged VLAN ID: 100-200
|
||||
if taggedRegex := regexp.MustCompile(`Tagged VLAN ID\s*:\s*(\S+)`); taggedRegex.MatchString(line) {
|
||||
tagged := taggedRegex.FindStringSubmatch(line)[1]
|
||||
if tagged != "none" && tagged != "--" {
|
||||
if currentInterface.VLAN != "" {
|
||||
currentInterface.VLAN = currentInterface.VLAN + " (T:" + tagged + ")"
|
||||
} else {
|
||||
currentInterface.VLAN = "T:" + tagged
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,19 +344,133 @@ func normalizeMACFormat(mac string) string {
|
||||
return mac
|
||||
}
|
||||
|
||||
// expandH3CInterfaceName 将H3C brief中的缩写接口名展开为全名
|
||||
func expandH3CInterfaceName(shortName string) string {
|
||||
// GE1/0/10 -> GigabitEthernet1/0/10
|
||||
if strings.HasPrefix(shortName, "GE") {
|
||||
return "GigabitEthernet" + shortName[2:]
|
||||
}
|
||||
// TGE1/0/52 -> Ten-GigabitEthernet1/0/52
|
||||
if strings.HasPrefix(shortName, "TGE") {
|
||||
return "Ten-GigabitEthernet" + shortName[3:]
|
||||
}
|
||||
// FGE1/0/53 -> FortyGigE1/0/53
|
||||
if strings.HasPrefix(shortName, "FGE") {
|
||||
return "FortyGigE" + shortName[3:]
|
||||
}
|
||||
// HGE1/0/1 -> HundredGigE1/0/1
|
||||
if strings.HasPrefix(shortName, "HGE") {
|
||||
return "HundredGigE" + shortName[3:]
|
||||
}
|
||||
// XGE1/0/1 -> Ten-GigabitEthernet1/0/1 (有些型号用XGE)
|
||||
if strings.HasPrefix(shortName, "XGE") {
|
||||
return "Ten-GigabitEthernet" + shortName[3:]
|
||||
}
|
||||
// Vlan8 -> Vlan-interface8
|
||||
if strings.HasPrefix(shortName, "Vlan") {
|
||||
return "Vlan-interface" + shortName[4:]
|
||||
}
|
||||
return shortName
|
||||
}
|
||||
|
||||
func (p *H3CParser) parseInterfaceBrief(output string) map[string]models.Interface {
|
||||
interfaces := make(map[string]models.Interface)
|
||||
lines := strings.Split(output, "\n")
|
||||
|
||||
// 状态机: 0=未开始, 1=route mode数据, 2=bridge mode数据
|
||||
mode := 0
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) >= 4 {
|
||||
iface := models.Interface{
|
||||
Name: fields[0],
|
||||
IP: fields[1],
|
||||
Status: strings.ToLower(fields[3]),
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if trimmed == "" || strings.HasPrefix(trimmed, "---") {
|
||||
continue
|
||||
}
|
||||
|
||||
// 检测 route mode section
|
||||
if strings.Contains(trimmed, "route mode") {
|
||||
mode = 1
|
||||
continue
|
||||
}
|
||||
// 检测 bridge mode section
|
||||
if strings.Contains(trimmed, "bridge mode") {
|
||||
mode = 2
|
||||
continue
|
||||
}
|
||||
|
||||
// 跳过表头行
|
||||
fields := strings.Fields(trimmed)
|
||||
if len(fields) > 0 && (fields[0] == "Interface" || fields[0] == "interface") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Route mode: Interface Link Protocol Primary IP Description
|
||||
if mode == 1 && len(fields) >= 3 {
|
||||
shortName := fields[0]
|
||||
fullName := expandH3CInterfaceName(shortName)
|
||||
ip := ""
|
||||
if len(fields) >= 4 && fields[3] != "--" {
|
||||
ip = fields[3]
|
||||
}
|
||||
interfaces[iface.Name] = iface
|
||||
iface := models.Interface{
|
||||
Name: fullName,
|
||||
Status: strings.ToLower(fields[1]),
|
||||
IP: ip,
|
||||
}
|
||||
interfaces[fullName] = iface
|
||||
}
|
||||
|
||||
// Bridge mode: Interface Link Speed Duplex Type PVID Description
|
||||
if mode == 2 && len(fields) >= 5 {
|
||||
shortName := fields[0]
|
||||
fullName := expandH3CInterfaceName(shortName)
|
||||
|
||||
speed := fields[2]
|
||||
if speed == "auto" {
|
||||
speed = "auto"
|
||||
}
|
||||
|
||||
duplex := fields[3]
|
||||
switch duplex {
|
||||
case "A":
|
||||
duplex = "auto"
|
||||
case "F(a)", "F":
|
||||
duplex = "full"
|
||||
case "H":
|
||||
duplex = "half"
|
||||
}
|
||||
|
||||
// VLAN: Type(A/T/H) + PVID
|
||||
vlan := ""
|
||||
if len(fields) >= 5 {
|
||||
linkType := fields[4]
|
||||
switch linkType {
|
||||
case "A":
|
||||
vlan = "Access"
|
||||
case "T":
|
||||
vlan = "Trunk"
|
||||
case "H":
|
||||
vlan = "Hybrid"
|
||||
}
|
||||
}
|
||||
if len(fields) >= 6 {
|
||||
vlan = vlan + " " + fields[5]
|
||||
}
|
||||
|
||||
iface := models.Interface{
|
||||
Name: fullName,
|
||||
Status: strings.ToLower(fields[1]),
|
||||
Speed: speed,
|
||||
Duplex: duplex,
|
||||
VLAN: strings.TrimSpace(vlan),
|
||||
}
|
||||
|
||||
// 如果 route mode 已有此接口(通常不会有), 保留 IP 信息
|
||||
if existing, ok := interfaces[fullName]; ok {
|
||||
if existing.IP != "" {
|
||||
iface.IP = existing.IP
|
||||
}
|
||||
}
|
||||
interfaces[fullName] = iface
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Referens i nytt ärende
Block a user