279 líneas
7.4 KiB
Go
279 líneas
7.4 KiB
Go
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
|
|
}
|