huawei.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. package device
  2. import (
  3. "bufio"
  4. "fmt"
  5. "network-topology-discovery/pkg/models"
  6. "regexp"
  7. "strings"
  8. )
  9. // HuaweiParser 华为设备解析器
  10. type HuaweiParser struct {
  11. BaseParser
  12. }
  13. // GetCommands 获取华为设备命令列表
  14. func (p *HuaweiParser) GetCommands() []string {
  15. return []string{
  16. "display version",
  17. "display interface",
  18. "display interface brief",
  19. "display lldp neighbor",
  20. }
  21. }
  22. // Parse 解析华为设备输出
  23. func (p *HuaweiParser) Parse(device *models.Device, outputs []string) error {
  24. if len(outputs) < 4 {
  25. return fmt.Errorf("insufficient command outputs")
  26. }
  27. p.parseVersion(device, outputs[0])
  28. device.Interfaces = p.parseInterfaces(outputs[1], outputs[2])
  29. device.Neighbors = p.parseNeighbors(outputs[3])
  30. return nil
  31. }
  32. func (p *HuaweiParser) parseVersion(device *models.Device, output string) {
  33. // 提取主机名
  34. hostnameRegex := regexp.MustCompile(`<(\S+)>`)
  35. if matches := hostnameRegex.FindStringSubmatch(output); len(matches) > 1 {
  36. device.Hostname = matches[1]
  37. }
  38. // 提取版本信息
  39. if strings.Contains(output, "VRP") {
  40. lines := strings.Split(output, "\n")
  41. for _, line := range lines {
  42. if strings.Contains(line, "VRP") {
  43. device.OSVersion = strings.TrimSpace(line)
  44. break
  45. }
  46. }
  47. }
  48. // 提取运行时间
  49. uptimeRegex := regexp.MustCompile(`uptime is\s+(\d+\s+\S+)`)
  50. if matches := uptimeRegex.FindStringSubmatch(output); len(matches) > 1 {
  51. device.Uptime = matches[1]
  52. }
  53. }
  54. func (p *HuaweiParser) parseInterfaces(interfaceOutput, briefOutput string) []models.Interface {
  55. var interfaces []models.Interface
  56. briefMap := p.parseInterfaceBrief(briefOutput)
  57. scanner := bufio.NewScanner(strings.NewReader(interfaceOutput))
  58. var currentInterface *models.Interface
  59. for scanner.Scan() {
  60. line := scanner.Text()
  61. // 匹配接口名称
  62. if nameRegex := regexp.MustCompile(`^(\S+)\s+current state\s+(UP|DOWN)`); nameRegex.MatchString(line) {
  63. if currentInterface != nil {
  64. interfaces = append(interfaces, *currentInterface)
  65. }
  66. matches := nameRegex.FindStringSubmatch(line)
  67. currentInterface = &models.Interface{
  68. Name: matches[1],
  69. Status: strings.ToLower(matches[2]),
  70. }
  71. if brief, ok := briefMap[currentInterface.Name]; ok {
  72. currentInterface.IP = brief.IP
  73. if currentInterface.Speed == "" {
  74. currentInterface.Speed = brief.Speed
  75. }
  76. if currentInterface.Duplex == "" {
  77. currentInterface.Duplex = brief.Duplex
  78. }
  79. if currentInterface.VLAN == "" {
  80. currentInterface.VLAN = brief.VLAN
  81. }
  82. }
  83. }
  84. if currentInterface != nil {
  85. // 描述
  86. if descRegex := regexp.MustCompile(`Description:\s+(.+)`); descRegex.MatchString(line) {
  87. currentInterface.Description = descRegex.FindStringSubmatch(line)[1]
  88. }
  89. // MAC地址
  90. if macRegex := regexp.MustCompile(`Hardware address is\s+(\S+)`); macRegex.MatchString(line) {
  91. currentInterface.MAC = macRegex.FindStringSubmatch(line)[1]
  92. }
  93. // IP地址
  94. if ipRegex := regexp.MustCompile(`Internet Address is\s+(\d+\.\d+\.\d+\.\d+),\s+Subnet mask is\s+(\d+\.\d+\.\d+\.\d+)`); ipRegex.MatchString(line) {
  95. matches := ipRegex.FindStringSubmatch(line)
  96. currentInterface.IP = matches[1]
  97. currentInterface.Mask = matches[2]
  98. }
  99. // 带宽格式1: Speed : 1000Mbps
  100. if speedRegex := regexp.MustCompile(`Speed\s*:\s*(\d+)\s*(Kbps|Mbps|Gbps)`); speedRegex.MatchString(line) {
  101. matches := speedRegex.FindStringSubmatch(line)
  102. currentInterface.Speed = matches[1] + matches[2]
  103. }
  104. // 带宽格式2: BW 1000000 Kbit
  105. if speedRegex2 := regexp.MustCompile(`BW\s+(\d+)\s+(Kbit|Mbit|Gbit)`); speedRegex2.MatchString(line) && currentInterface.Speed == "" {
  106. matches := speedRegex2.FindStringSubmatch(line)
  107. currentInterface.Speed = matches[1] + " " + matches[2]
  108. }
  109. // VLAN信息: Port link-type
  110. if linkTypeRegex := regexp.MustCompile(`Port link-type\s*:\s*(\S+)`); linkTypeRegex.MatchString(line) {
  111. currentInterface.VLAN = linkTypeRegex.FindStringSubmatch(line)[1]
  112. }
  113. if untaggedRegex := regexp.MustCompile(`Untagged VLAN ID\s*:\s*(\S+)`); untaggedRegex.MatchString(line) {
  114. untagged := untaggedRegex.FindStringSubmatch(line)[1]
  115. if untagged != "none" && untagged != "--" {
  116. if currentInterface.VLAN != "" {
  117. currentInterface.VLAN = currentInterface.VLAN + " " + untagged
  118. } else {
  119. currentInterface.VLAN = untagged
  120. }
  121. }
  122. }
  123. if taggedRegex := regexp.MustCompile(`Tagged VLAN ID\s*:\s*(\S+)`); taggedRegex.MatchString(line) {
  124. tagged := taggedRegex.FindStringSubmatch(line)[1]
  125. if tagged != "none" && tagged != "--" {
  126. if currentInterface.VLAN != "" {
  127. currentInterface.VLAN = currentInterface.VLAN + " (T:" + tagged + ")"
  128. } else {
  129. currentInterface.VLAN = "T:" + tagged
  130. }
  131. }
  132. }
  133. }
  134. }
  135. if currentInterface != nil {
  136. interfaces = append(interfaces, *currentInterface)
  137. }
  138. return interfaces
  139. }
  140. func (p *HuaweiParser) parseInterfaceBrief(output string) map[string]models.Interface {
  141. interfaces := make(map[string]models.Interface)
  142. lines := strings.Split(output, "\n")
  143. mode := 0 // 0=未开始, 1=route mode, 2=bridge mode
  144. for _, line := range lines {
  145. trimmed := strings.TrimSpace(line)
  146. if trimmed == "" || strings.HasPrefix(trimmed, "---") {
  147. continue
  148. }
  149. if strings.Contains(trimmed, "route mode") {
  150. mode = 1
  151. continue
  152. }
  153. if strings.Contains(trimmed, "bridge mode") {
  154. mode = 2
  155. continue
  156. }
  157. fields := strings.Fields(trimmed)
  158. if len(fields) > 0 && (fields[0] == "Interface" || fields[0] == "interface") {
  159. continue
  160. }
  161. // Route mode: Interface Link Protocol Primary IP Description
  162. if mode == 1 && len(fields) >= 3 {
  163. fullName := expandH3CInterfaceName(fields[0])
  164. ip := ""
  165. if len(fields) >= 4 && fields[3] != "--" {
  166. ip = fields[3]
  167. }
  168. iface := models.Interface{
  169. Name: fullName,
  170. Status: strings.ToLower(fields[1]),
  171. IP: ip,
  172. }
  173. interfaces[fullName] = iface
  174. }
  175. // Bridge mode: Interface Link Speed Duplex Type PVID Description
  176. if mode == 2 && len(fields) >= 5 {
  177. fullName := expandH3CInterfaceName(fields[0])
  178. speed := fields[2]
  179. duplex := fields[3]
  180. switch duplex {
  181. case "A":
  182. duplex = "auto"
  183. case "F(a)", "F":
  184. duplex = "full"
  185. case "H":
  186. duplex = "half"
  187. }
  188. vlan := ""
  189. if len(fields) >= 5 {
  190. switch fields[4] {
  191. case "A":
  192. vlan = "Access"
  193. case "T":
  194. vlan = "Trunk"
  195. case "H":
  196. vlan = "Hybrid"
  197. }
  198. }
  199. if len(fields) >= 6 {
  200. vlan = vlan + " " + fields[5]
  201. }
  202. iface := models.Interface{
  203. Name: fullName,
  204. Status: strings.ToLower(fields[1]),
  205. Speed: speed,
  206. Duplex: duplex,
  207. VLAN: strings.TrimSpace(vlan),
  208. }
  209. if existing, ok := interfaces[fullName]; ok {
  210. if existing.IP != "" {
  211. iface.IP = existing.IP
  212. }
  213. }
  214. interfaces[fullName] = iface
  215. }
  216. }
  217. return interfaces
  218. }
  219. func (p *HuaweiParser) parseNeighbors(output string) []models.Neighbor {
  220. var neighbors []models.Neighbor
  221. scanner := bufio.NewScanner(strings.NewReader(output))
  222. var currentNeighbor *models.Neighbor
  223. for scanner.Scan() {
  224. line := scanner.Text()
  225. // 跳过标题行
  226. if strings.Contains(line, "Local Interface") || strings.Contains(line, "-----") {
  227. continue
  228. }
  229. fields := strings.Fields(line)
  230. if len(fields) >= 5 {
  231. currentNeighbor = &models.Neighbor{
  232. LocalInterface: fields[0],
  233. RemoteDevice: fields[2],
  234. RemoteInterface: fields[4],
  235. Protocol: "LLDP",
  236. }
  237. neighbors = append(neighbors, *currentNeighbor)
  238. }
  239. }
  240. return neighbors
  241. }