package device import ( "bufio" "fmt" "network-topology-discovery/pkg/models" "regexp" "strings" ) // HuaweiParser 华为设备解析器 type HuaweiParser struct { BaseParser } // GetCommands 获取华为设备命令列表 func (p *HuaweiParser) GetCommands() []string { return []string{ "display version", "display interface", "display interface brief", "display lldp neighbor", } } // Parse 解析华为设备输出 func (p *HuaweiParser) Parse(device *models.Device, outputs []string) error { if len(outputs) < 4 { return fmt.Errorf("insufficient command outputs") } p.parseVersion(device, outputs[0]) device.Interfaces = p.parseInterfaces(outputs[1], outputs[2]) device.Neighbors = p.parseNeighbors(outputs[3]) return nil } func (p *HuaweiParser) parseVersion(device *models.Device, output string) { // 提取主机名 hostnameRegex := regexp.MustCompile(`<(\S+)>`) if matches := hostnameRegex.FindStringSubmatch(output); len(matches) > 1 { device.Hostname = matches[1] } // 提取版本信息 if strings.Contains(output, "VRP") { lines := strings.Split(output, "\n") for _, line := range lines { if strings.Contains(line, "VRP") { device.OSVersion = strings.TrimSpace(line) break } } } // 提取运行时间 uptimeRegex := regexp.MustCompile(`uptime is\s+(\d+\s+\S+)`) if matches := uptimeRegex.FindStringSubmatch(output); len(matches) > 1 { device.Uptime = matches[1] } } func (p *HuaweiParser) parseInterfaces(interfaceOutput, briefOutput string) []models.Interface { var interfaces []models.Interface briefMap := p.parseInterfaceBrief(briefOutput) scanner := bufio.NewScanner(strings.NewReader(interfaceOutput)) var currentInterface *models.Interface for scanner.Scan() { line := scanner.Text() // 匹配接口名称 if nameRegex := regexp.MustCompile(`^(\S+)\s+current state\s+(UP|DOWN)`); nameRegex.MatchString(line) { if currentInterface != nil { interfaces = append(interfaces, *currentInterface) } matches := nameRegex.FindStringSubmatch(line) currentInterface = &models.Interface{ Name: matches[1], Status: strings.ToLower(matches[2]), } 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 } } } if currentInterface != nil { // 描述 if descRegex := regexp.MustCompile(`Description:\s+(.+)`); descRegex.MatchString(line) { currentInterface.Description = descRegex.FindStringSubmatch(line)[1] } // MAC地址 if macRegex := regexp.MustCompile(`Hardware address is\s+(\S+)`); macRegex.MatchString(line) { currentInterface.MAC = macRegex.FindStringSubmatch(line)[1] } // IP地址 if ipRegex := regexp.MustCompile(`Internet Address is\s+(\d+\.\d+\.\d+\.\d+),\s+Subnet mask is\s+(\d+\.\d+\.\d+\.\d+)`); ipRegex.MatchString(line) { matches := ipRegex.FindStringSubmatch(line) currentInterface.IP = matches[1] currentInterface.Mask = matches[2] } // 带宽格式1: Speed : 1000Mbps 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] } // 带宽格式2: BW 1000000 Kbit if speedRegex2 := regexp.MustCompile(`BW\s+(\d+)\s+(Kbit|Mbit|Gbit)`); speedRegex2.MatchString(line) && currentInterface.Speed == "" { matches := speedRegex2.FindStringSubmatch(line) currentInterface.Speed = matches[1] + " " + matches[2] } // VLAN信息: Port link-type if linkTypeRegex := regexp.MustCompile(`Port link-type\s*:\s*(\S+)`); linkTypeRegex.MatchString(line) { currentInterface.VLAN = linkTypeRegex.FindStringSubmatch(line)[1] } 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 } } } 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 } } } } } if currentInterface != nil { interfaces = append(interfaces, *currentInterface) } return interfaces } func (p *HuaweiParser) parseInterfaceBrief(output string) map[string]models.Interface { interfaces := make(map[string]models.Interface) lines := strings.Split(output, "\n") mode := 0 // 0=未开始, 1=route mode, 2=bridge mode for _, line := range lines { trimmed := strings.TrimSpace(line) if trimmed == "" || strings.HasPrefix(trimmed, "---") { continue } if strings.Contains(trimmed, "route mode") { mode = 1 continue } 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 { fullName := expandH3CInterfaceName(fields[0]) ip := "" if len(fields) >= 4 && fields[3] != "--" { ip = fields[3] } 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 { fullName := expandH3CInterfaceName(fields[0]) speed := fields[2] duplex := fields[3] switch duplex { case "A": duplex = "auto" case "F(a)", "F": duplex = "full" case "H": duplex = "half" } vlan := "" if len(fields) >= 5 { switch fields[4] { 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), } if existing, ok := interfaces[fullName]; ok { if existing.IP != "" { iface.IP = existing.IP } } interfaces[fullName] = iface } } return interfaces } func (p *HuaweiParser) parseNeighbors(output string) []models.Neighbor { var neighbors []models.Neighbor scanner := bufio.NewScanner(strings.NewReader(output)) var currentNeighbor *models.Neighbor for scanner.Scan() { line := scanner.Text() // 跳过标题行 if strings.Contains(line, "Local Interface") || strings.Contains(line, "-----") { continue } fields := strings.Fields(line) if len(fields) >= 5 { currentNeighbor = &models.Neighbor{ LocalInterface: fields[0], RemoteDevice: fields[2], RemoteInterface: fields[4], Protocol: "LLDP", } neighbors = append(neighbors, *currentNeighbor) } } return neighbors }