completer.go 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288
  1. package cli
  2. import (
  3. "sort"
  4. "strings"
  5. "sync"
  6. "time"
  7. )
  8. // TerminalWriter is an interface satisfied by *terminal.Terminal and mock terminals.
  9. type TerminalWriter interface {
  10. Write(buf []byte) (n int, err error)
  11. }
  12. // Node represents a command or subsection in the CLI hierarchy.
  13. type Node struct {
  14. Name string
  15. Aliases []string
  16. Description string
  17. Children []*Node
  18. Dynamic func() []string
  19. }
  20. // AddChild appends a child node to this node and returns the child.
  21. func (n *Node) AddChild(child *Node) *Node {
  22. n.Children = append(n.Children, child)
  23. return child
  24. }
  25. // FindChild searches for a child matching token by Name, Aliases, or unique prefix.
  26. func (n *Node) FindChild(token string) *Node {
  27. token = strings.ToLower(token)
  28. // 1. Exact Name match
  29. for _, ch := range n.Children {
  30. if strings.ToLower(ch.Name) == token {
  31. return ch
  32. }
  33. }
  34. // 2. Exact Aliases match
  35. for _, ch := range n.Children {
  36. for _, a := range ch.Aliases {
  37. if strings.ToLower(a) == token {
  38. return ch
  39. }
  40. }
  41. }
  42. // 3. Unique prefix match among children (excluding parameters ending in '=')
  43. var match *Node
  44. for _, ch := range n.Children {
  45. if !strings.HasSuffix(ch.Name, "=") && strings.HasPrefix(strings.ToLower(ch.Name), token) {
  46. if match != nil {
  47. return nil // Ambiguous
  48. }
  49. match = ch
  50. }
  51. }
  52. return match
  53. }
  54. // Completer manages tab-completion state and double-tab candidate listing.
  55. type Completer struct {
  56. term TerminalWriter
  57. executor *Executor
  58. root *Node
  59. termWidth int
  60. mu sync.Mutex
  61. lastTabTime time.Time
  62. lastTabLine string
  63. lastTabPos int
  64. }
  65. // NewCompleter creates and initializes a Completer with the full command tree.
  66. func NewCompleter(term TerminalWriter, executor *Executor) *Completer {
  67. c := &Completer{
  68. term: term,
  69. executor: executor,
  70. termWidth: 80,
  71. }
  72. c.root = c.buildCommandTree()
  73. return c
  74. }
  75. // SetWidth sets the terminal width for candidate column formatting.
  76. func (c *Completer) SetWidth(w int) {
  77. c.mu.Lock()
  78. defer c.mu.Unlock()
  79. if w > 20 {
  80. c.termWidth = w
  81. }
  82. }
  83. // Callback is the AutoCompleteCallback for golang.org/x/term.Terminal.
  84. func (c *Completer) Callback(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
  85. if key != '\t' {
  86. return "", 0, false
  87. }
  88. c.mu.Lock()
  89. defer c.mu.Unlock()
  90. candidates, prefixBeforeWord, currentWord, isParamVal, paramPrefix := c.getCompletions(line, pos)
  91. if len(candidates) == 0 {
  92. // Nothing to complete
  93. c.lastTabTime = time.Time{}
  94. c.lastTabLine = ""
  95. c.lastTabPos = 0
  96. return "", 0, false
  97. }
  98. suffix := ""
  99. if pos < len(line) {
  100. suffix = line[pos:]
  101. }
  102. // Single match: complete it immediately!
  103. if len(candidates) == 1 {
  104. c.lastTabTime = time.Time{}
  105. c.lastTabLine = ""
  106. c.lastTabPos = 0
  107. match := candidates[0]
  108. completedToken := match
  109. if isParamVal {
  110. completedToken = paramPrefix + match + " "
  111. } else if strings.HasSuffix(match, "=") {
  112. // Parameter key, e.g. "address=" -> no trailing space
  113. completedToken = match
  114. } else {
  115. completedToken = match + " "
  116. }
  117. newLine = prefixBeforeWord + completedToken + suffix
  118. newPos = len(prefixBeforeWord) + len(completedToken)
  119. return newLine, newPos, true
  120. }
  121. // Multiple candidates: find Longest Common Prefix (LCP)
  122. lcp := longestCommonPrefix(candidates)
  123. targetWord := currentWord
  124. if isParamVal {
  125. targetWord = currentWord[len(paramPrefix):]
  126. }
  127. // If LCP can extend the current input, expand it
  128. if len(lcp) > len(targetWord) {
  129. extendedToken := lcp
  130. if isParamVal {
  131. extendedToken = paramPrefix + lcp
  132. }
  133. newLine = prefixBeforeWord + extendedToken + suffix
  134. newPos = len(prefixBeforeWord) + len(extendedToken)
  135. // Record tab state so an immediate second tab shows remaining options
  136. c.lastTabTime = time.Now()
  137. c.lastTabLine = newLine
  138. c.lastTabPos = newPos
  139. return newLine, newPos, true
  140. }
  141. // LCP cannot extend input further: check for 2nd Tab (within 1000ms)
  142. isSecondTab := time.Since(c.lastTabTime) <= 1000*time.Millisecond &&
  143. c.lastTabLine == line &&
  144. c.lastTabPos == pos
  145. if !isSecondTab {
  146. // 1st Tab: Record state and wait for potential 2nd Tab
  147. c.lastTabTime = time.Now()
  148. c.lastTabLine = line
  149. c.lastTabPos = pos
  150. return line, pos, true
  151. }
  152. // 2nd Tab: DISPLAY ALL CANDIDATES!
  153. c.lastTabTime = time.Time{}
  154. c.lastTabLine = ""
  155. c.lastTabPos = 0
  156. if c.term != nil {
  157. formatted := c.formatCandidates(candidates)
  158. _, _ = c.term.Write([]byte(formatted))
  159. }
  160. return line, pos, true
  161. }
  162. // getCompletions computes the candidate completions for the current cursor context.
  163. func (c *Completer) getCompletions(line string, pos int) (candidates []string, prefixBeforeWord string, currentWord string, isParamVal bool, paramPrefix string) {
  164. if pos > len(line) {
  165. pos = len(line)
  166. }
  167. input := line[:pos]
  168. lastSpace := strings.LastIndexAny(input, " \t")
  169. if lastSpace == -1 {
  170. // Single word so far (or empty)
  171. if strings.HasPrefix(input, "/") {
  172. prefixBeforeWord = "/"
  173. currentWord = input[1:]
  174. } else {
  175. prefixBeforeWord = ""
  176. currentWord = input
  177. }
  178. } else {
  179. prefixBeforeWord = input[:lastSpace+1]
  180. currentWord = input[lastSpace+1:]
  181. }
  182. // Parse preceding words to navigate tree
  183. rawPrefix := strings.TrimSpace(prefixBeforeWord)
  184. hasSlash := strings.HasPrefix(rawPrefix, "/")
  185. if hasSlash {
  186. rawPrefix = strings.TrimPrefix(rawPrefix, "/")
  187. }
  188. var precedingTokens []string
  189. if rawPrefix != "" {
  190. precedingTokens = strings.Fields(rawPrefix)
  191. }
  192. // Traverse tree to find current node and track used parameter keys
  193. currNode := c.root
  194. usedParams := make(map[string]bool)
  195. for _, token := range precedingTokens {
  196. if idx := strings.Index(token, "="); idx != -1 {
  197. // Parameter key=value, stay at current node
  198. key := strings.ToLower(token[:idx+1])
  199. usedParams[key] = true
  200. continue
  201. }
  202. next := currNode.FindChild(token)
  203. if next != nil {
  204. currNode = next
  205. }
  206. }
  207. // Check if user is typing a parameter value (e.g. "interface=eth" or "role=")
  208. if idx := strings.Index(currentWord, "="); idx != -1 {
  209. paramKey := strings.ToLower(currentWord[:idx+1])
  210. paramVal := strings.ToLower(currentWord[idx+1:])
  211. paramChild := currNode.FindChild(paramKey)
  212. if paramChild != nil && paramChild.Dynamic != nil {
  213. isParamVal = true
  214. paramPrefix = currentWord[:idx+1]
  215. for _, val := range paramChild.Dynamic() {
  216. if strings.HasPrefix(strings.ToLower(val), paramVal) {
  217. candidates = append(candidates, val)
  218. }
  219. }
  220. sort.Strings(candidates)
  221. return
  222. }
  223. }
  224. // Gather child commands and parameters
  225. search := strings.ToLower(currentWord)
  226. for _, ch := range currNode.Children {
  227. nameLower := strings.ToLower(ch.Name)
  228. if strings.HasSuffix(nameLower, "=") {
  229. if usedParams[nameLower] {
  230. continue // parameter already provided
  231. }
  232. }
  233. if strings.HasPrefix(nameLower, search) {
  234. candidates = append(candidates, ch.Name)
  235. }
  236. }
  237. // Check if current node has dynamic positional choices (e.g. interface names or service names)
  238. if currNode.Dynamic != nil {
  239. for _, dyn := range currNode.Dynamic() {
  240. if strings.HasPrefix(strings.ToLower(dyn), search) {
  241. candidates = append(candidates, dyn)
  242. }
  243. }
  244. }
  245. // Deduplicate and sort
  246. candidates = uniqueStrings(candidates)
  247. sort.Strings(candidates)
  248. return
  249. }
  250. func (c *Completer) formatCandidates(candidates []string) string {
  251. if len(candidates) == 0 {
  252. return ""
  253. }
  254. termWidth := c.termWidth
  255. if termWidth < 40 {
  256. termWidth = 80
  257. }
  258. maxLen := 0
  259. for _, cand := range candidates {
  260. if len(cand) > maxLen {
  261. maxLen = len(cand)
  262. }
  263. }
  264. colWidth := maxLen + 3
  265. if colWidth < 10 {
  266. colWidth = 10
  267. }
  268. numCols := (termWidth - 4) / colWidth
  269. if numCols < 1 {
  270. numCols = 1
  271. }
  272. if numCols > len(candidates) {
  273. numCols = len(candidates)
  274. }
  275. numRows := (len(candidates) + numCols - 1) / numCols
  276. var sb strings.Builder
  277. sb.WriteString("\n")
  278. for r := 0; r < numRows; r++ {
  279. sb.WriteString(" ")
  280. for col := 0; col < numCols; col++ {
  281. idx := col*numRows + r
  282. if idx < len(candidates) {
  283. cand := candidates[idx]
  284. colored := colorizeCandidate(cand)
  285. sb.WriteString(colored)
  286. if col < numCols-1 && (col+1)*numRows+r < len(candidates) {
  287. pad := colWidth - len(cand)
  288. if pad > 0 {
  289. sb.WriteString(strings.Repeat(" ", pad))
  290. }
  291. }
  292. }
  293. }
  294. sb.WriteString("\n")
  295. }
  296. return sb.String()
  297. }
  298. func colorizeCandidate(s string) string {
  299. if strings.HasSuffix(s, "=") {
  300. return ColorYellow + s + ColorReset
  301. }
  302. switch s {
  303. case "print", "add", "remove", "enable", "disable", "set", "reboot", "flush", "renew", "release", "connect", "disconnect", "status":
  304. return ColorGreen + s + ColorReset
  305. case "interface", "ip", "routing", "tool", "log", "user", "system", "certificate", "certificates", "ppp",
  306. "vlan", "bridge", "wireguard", "eoip", "vxlan", "vrrp", "pppoe-client", "l2tp-server", "pptp-server", "pppoe-server",
  307. "profile", "secret", "active",
  308. "address", "route", "dhcp-client", "dhcp-server", "dns", "firewall", "service", "reverse-proxy", "sdwan", "sd-wan", "member", "members",
  309. "filter", "nat", "mangle", "address-list", "bgp", "ospf", "port", "peers", "lease", "network", "cache", "static", "rule", "table", "instance", "area", "neighbor", "resource", "identity":
  310. return ColorCyan + s + ColorReset
  311. case "www", "www-ssl", "ssh", "telnet", "web", "web-ssl":
  312. return ColorMagenta + s + ColorReset
  313. default:
  314. return ColorWhite + s + ColorReset
  315. }
  316. }
  317. func longestCommonPrefix(strs []string) string {
  318. if len(strs) == 0 {
  319. return ""
  320. }
  321. prefix := strs[0]
  322. for _, s := range strs[1:] {
  323. for !strings.HasPrefix(strings.ToLower(s), strings.ToLower(prefix)) {
  324. if len(prefix) == 0 {
  325. return ""
  326. }
  327. prefix = prefix[:len(prefix)-1]
  328. }
  329. }
  330. return prefix
  331. }
  332. func uniqueStrings(slice []string) []string {
  333. seen := make(map[string]bool)
  334. var result []string
  335. for _, s := range slice {
  336. if !seen[s] {
  337. seen[s] = true
  338. result = append(result, s)
  339. }
  340. }
  341. return result
  342. }
  343. // buildCommandTree creates the full tree of commands, subsections, actions, parameters, and dynamic providers.
  344. func (c *Completer) buildCommandTree() *Node {
  345. root := &Node{Name: "root"}
  346. // Helper for dynamic interfaces
  347. dynInterfaces := func() []string {
  348. if c.executor != nil && c.executor.netMgr != nil {
  349. ifaces, err := c.executor.netMgr.ListInterfaces()
  350. if err == nil {
  351. var names []string
  352. for _, iface := range ifaces {
  353. names = append(names, iface.Name)
  354. }
  355. return names
  356. }
  357. }
  358. return []string{"eth0", "eth1", "lo"}
  359. }
  360. // Helper for services
  361. dynServices := func() []string {
  362. if c.executor != nil && c.executor.servicesMgr != nil {
  363. var names []string
  364. for _, s := range c.executor.servicesMgr.ListServices() {
  365. names = append(names, s.Name)
  366. }
  367. return names
  368. }
  369. return []string{"www", "www-ssl", "telnet", "ssh"}
  370. }
  371. // Helper for certificates
  372. dynCertificates := func() []string {
  373. if c.executor != nil && c.executor.certMgr != nil {
  374. var names []string
  375. for _, cert := range c.executor.certMgr.ListCertificates() {
  376. names = append(names, cert.Name)
  377. }
  378. return names
  379. }
  380. return nil
  381. }
  382. dynCAs := func() []string {
  383. if c.executor != nil && c.executor.certMgr != nil {
  384. var names []string
  385. for _, cert := range c.executor.certMgr.ListCertificates() {
  386. if cert.IsCA {
  387. names = append(names, cert.Name)
  388. }
  389. }
  390. return names
  391. }
  392. return nil
  393. }
  394. dynSDWANGroups := func() []string {
  395. if c.executor != nil && c.executor.sdwanMgr != nil {
  396. var names []string
  397. for _, g := range c.executor.sdwanMgr.List() {
  398. names = append(names, g.Name)
  399. }
  400. return names
  401. }
  402. return nil
  403. }
  404. // Common standard actions
  405. addPrint := func(n *Node) {
  406. n.AddChild(&Node{Name: "print", Aliases: []string{"pr", "show", "list"}})
  407. }
  408. addRemove := func(n *Node) {
  409. n.AddChild(&Node{Name: "remove", Aliases: []string{"rem", "delete"}})
  410. }
  411. addEnableDisable := func(n *Node) {
  412. n.AddChild(&Node{Name: "enable"})
  413. n.AddChild(&Node{Name: "disable"})
  414. }
  415. // 1. /interface
  416. ifaceNode := root.AddChild(&Node{Name: "interface", Aliases: []string{"interfaces"}})
  417. addPrint(ifaceNode)
  418. ifaceNode.AddChild(&Node{Name: "enable", Dynamic: dynInterfaces})
  419. ifaceNode.AddChild(&Node{Name: "disable", Dynamic: dynInterfaces})
  420. ifaceSet := ifaceNode.AddChild(&Node{Name: "set", Dynamic: dynInterfaces})
  421. ifaceSet.AddChild(&Node{Name: "mtu="})
  422. // /interface vlan
  423. vlanNode := ifaceNode.AddChild(&Node{Name: "vlan", Aliases: []string{"vlans"}})
  424. addPrint(vlanNode)
  425. addRemove(vlanNode)
  426. vlanAdd := vlanNode.AddChild(&Node{Name: "add"})
  427. vlanAdd.AddChild(&Node{Name: "name="})
  428. vlanAdd.AddChild(&Node{Name: "vlan-id="})
  429. vlanAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  430. // /interface bridge
  431. bridgeNode := ifaceNode.AddChild(&Node{Name: "bridge", Aliases: []string{"bridges"}})
  432. addPrint(bridgeNode)
  433. addRemove(bridgeNode)
  434. addEnableDisable(bridgeNode)
  435. bridgeAdd := bridgeNode.AddChild(&Node{Name: "add"})
  436. bridgeAdd.AddChild(&Node{Name: "name="})
  437. // /interface bridge port
  438. bridgePortNode := bridgeNode.AddChild(&Node{Name: "port", Aliases: []string{"ports"}})
  439. addPrint(bridgePortNode)
  440. addRemove(bridgePortNode)
  441. addEnableDisable(bridgePortNode)
  442. bridgePortAdd := bridgePortNode.AddChild(&Node{Name: "add"})
  443. bridgePortAdd.AddChild(&Node{Name: "bridge="})
  444. bridgePortAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  445. bridgePortAdd.AddChild(&Node{Name: "pvid="})
  446. // /interface wireguard
  447. wgNode := ifaceNode.AddChild(&Node{Name: "wireguard", Aliases: []string{"wg"}})
  448. addPrint(wgNode)
  449. addRemove(wgNode)
  450. addEnableDisable(wgNode)
  451. wgAdd := wgNode.AddChild(&Node{Name: "add"})
  452. wgAdd.AddChild(&Node{Name: "name="})
  453. wgAdd.AddChild(&Node{Name: "listen-port="})
  454. wgAdd.AddChild(&Node{Name: "private-key="})
  455. // /interface wireguard peers
  456. wgPeers := wgNode.AddChild(&Node{Name: "peers", Aliases: []string{"peer"}})
  457. addPrint(wgPeers)
  458. addRemove(wgPeers)
  459. wgPeersAdd := wgPeers.AddChild(&Node{Name: "add"})
  460. wgPeersAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  461. wgPeersAdd.AddChild(&Node{Name: "public-key="})
  462. wgPeersAdd.AddChild(&Node{Name: "allowed-address="})
  463. wgPeersAdd.AddChild(&Node{Name: "endpoint-address="})
  464. wgPeersAdd.AddChild(&Node{Name: "endpoint-port="})
  465. // /interface aerotun
  466. aero := ifaceNode.AddChild(&Node{Name: "aerotun"})
  467. names := func() []string {
  468. if c.executor.vpnMgr == nil || c.executor.vpnMgr.AeroTUN == nil {
  469. return nil
  470. }
  471. var out []string
  472. for _, inst := range c.executor.vpnMgr.AeroTUN.List() {
  473. out = append(out, inst.Name)
  474. }
  475. return out
  476. }
  477. aero.AddChild(&Node{Name: "print"}).AddChild(&Node{Name: "detail="})
  478. aero.AddChild(&Node{Name: "genkey"})
  479. for _, action := range []string{"add", "set", "remove", "enable", "disable", "start", "stop"} {
  480. n := aero.AddChild(&Node{Name: action, Dynamic: names})
  481. if action == "add" || action == "set" {
  482. for _, param := range []string{"name=", "protocol=", "mode=", "listen-port=", "endpoint=", "local-address=", "peer-address=", "peer-public-key=", "allowed-ips=", "psk=", "mtu=", "disabled=", "compression=", "default-route=", "comment="} {
  483. n.AddChild(&Node{Name: param})
  484. }
  485. }
  486. }
  487. // /interface eoip
  488. eoipNode := ifaceNode.AddChild(&Node{Name: "eoip"})
  489. addPrint(eoipNode)
  490. addRemove(eoipNode)
  491. addEnableDisable(eoipNode)
  492. eoipAdd := eoipNode.AddChild(&Node{Name: "add"})
  493. eoipAdd.AddChild(&Node{Name: "name="})
  494. eoipAdd.AddChild(&Node{Name: "remote-ip="})
  495. eoipAdd.AddChild(&Node{Name: "tunnel-id="})
  496. // /interface vxlan
  497. vxlanNode := ifaceNode.AddChild(&Node{Name: "vxlan"})
  498. addPrint(vxlanNode)
  499. addRemove(vxlanNode)
  500. addEnableDisable(vxlanNode)
  501. vxlanAdd := vxlanNode.AddChild(&Node{Name: "add"})
  502. for _, p := range []string{"name=", "vni=", "port=", "mtu=", "mac-address=", "local-address=", "remote-address=", "group=", "interface=", "learning=", "comment="} {
  503. vxlanAdd.AddChild(&Node{Name: p})
  504. }
  505. vxlanSet := vxlanNode.AddChild(&Node{Name: "set"})
  506. for _, p := range []string{"name=", "vni=", "port=", "mtu=", "mac-address=", "local-address=", "remote-address=", "group=", "interface=", "comment="} {
  507. vxlanSet.AddChild(&Node{Name: p})
  508. }
  509. vtepsNode := vxlanNode.AddChild(&Node{Name: "vteps"})
  510. addPrint(vtepsNode)
  511. addRemove(vtepsNode)
  512. vtepsAdd := vtepsNode.AddChild(&Node{Name: "add"})
  513. vtepsAdd.AddChild(&Node{Name: "interface="})
  514. vtepsAdd.AddChild(&Node{Name: "remote-ip="})
  515. vtepsAdd.AddChild(&Node{Name: "port="})
  516. vtepsAdd.AddChild(&Node{Name: "comment="})
  517. // /interface vrrp
  518. vrrpNode := ifaceNode.AddChild(&Node{Name: "vrrp"})
  519. addPrint(vrrpNode)
  520. addRemove(vrrpNode)
  521. addEnableDisable(vrrpNode)
  522. vrrpAdd := vrrpNode.AddChild(&Node{Name: "add"})
  523. vrrpAdd.AddChild(&Node{Name: "name="})
  524. vrrpAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  525. vrrpAdd.AddChild(&Node{Name: "vrid="})
  526. vrrpAdd.AddChild(&Node{Name: "priority="})
  527. // /interface pppoe-client
  528. pppoeNode := ifaceNode.AddChild(&Node{Name: "pppoe-client"})
  529. addPrint(pppoeNode)
  530. addRemove(pppoeNode)
  531. pppoeNode.AddChild(&Node{Name: "connect"})
  532. pppoeNode.AddChild(&Node{Name: "disconnect"})
  533. pppoeAdd := pppoeNode.AddChild(&Node{Name: "add"})
  534. pppoeAdd.AddChild(&Node{Name: "name="})
  535. pppoeAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  536. pppoeAdd.AddChild(&Node{Name: "user="})
  537. pppoeAdd.AddChild(&Node{Name: "password="})
  538. // /interface l2tp-server
  539. l2tpNode := ifaceNode.AddChild(&Node{Name: "l2tp-server"})
  540. addPrint(l2tpNode)
  541. l2tpSet := l2tpNode.AddChild(&Node{Name: "set"})
  542. l2tpSet.AddChild(&Node{Name: "enabled="})
  543. l2tpSet.AddChild(&Node{Name: "port="})
  544. l2tpSet.AddChild(&Node{Name: "default-profile="})
  545. l2tpSet.AddChild(&Node{Name: "use-ipsec="})
  546. l2tpSet.AddChild(&Node{Name: "ipsec-secret="})
  547. l2tpSrv := l2tpNode.AddChild(&Node{Name: "server"})
  548. addPrint(l2tpSrv)
  549. l2tpSrvSet := l2tpSrv.AddChild(&Node{Name: "set"})
  550. l2tpSrvSet.AddChild(&Node{Name: "enabled="})
  551. l2tpSrvSet.AddChild(&Node{Name: "port="})
  552. l2tpSrvSet.AddChild(&Node{Name: "default-profile="})
  553. l2tpSrvSet.AddChild(&Node{Name: "use-ipsec="})
  554. l2tpSrvSet.AddChild(&Node{Name: "ipsec-secret="})
  555. // /interface pptp-server
  556. pptpNode := ifaceNode.AddChild(&Node{Name: "pptp-server"})
  557. addPrint(pptpNode)
  558. pptpSet := pptpNode.AddChild(&Node{Name: "set"})
  559. pptpSet.AddChild(&Node{Name: "enabled="})
  560. pptpSet.AddChild(&Node{Name: "port="})
  561. pptpSet.AddChild(&Node{Name: "default-profile="})
  562. pptpSet.AddChild(&Node{Name: "require-mppe="})
  563. pptpSrv := pptpNode.AddChild(&Node{Name: "server"})
  564. addPrint(pptpSrv)
  565. pptpSrvSet := pptpSrv.AddChild(&Node{Name: "set"})
  566. pptpSrvSet.AddChild(&Node{Name: "enabled="})
  567. pptpSrvSet.AddChild(&Node{Name: "port="})
  568. pptpSrvSet.AddChild(&Node{Name: "default-profile="})
  569. pptpSrvSet.AddChild(&Node{Name: "require-mppe="})
  570. // /interface pppoe-server
  571. pppoeSrvNode := ifaceNode.AddChild(&Node{Name: "pppoe-server"})
  572. addPrint(pppoeSrvNode)
  573. pppoeSrvSet := pppoeSrvNode.AddChild(&Node{Name: "set"})
  574. pppoeSrvSet.AddChild(&Node{Name: "enabled="})
  575. pppoeSrvSet.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  576. pppoeSrvSet.AddChild(&Node{Name: "default-profile="})
  577. pppoeSrvSet.AddChild(&Node{Name: "service-name="})
  578. pppoeSrvSet.AddChild(&Node{Name: "one-session-per-host="})
  579. pppoeSrvSub := pppoeSrvNode.AddChild(&Node{Name: "server"})
  580. addPrint(pppoeSrvSub)
  581. pppoeSrvSubSet := pppoeSrvSub.AddChild(&Node{Name: "set"})
  582. pppoeSrvSubSet.AddChild(&Node{Name: "enabled="})
  583. pppoeSrvSubSet.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  584. pppoeSrvSubSet.AddChild(&Node{Name: "default-profile="})
  585. pppoeSrvSubSet.AddChild(&Node{Name: "service-name="})
  586. pppoeSrvSubSet.AddChild(&Node{Name: "one-session-per-host="})
  587. // /ppp
  588. pppNode := root.AddChild(&Node{Name: "ppp"})
  589. // /ppp profile
  590. profNode := pppNode.AddChild(&Node{Name: "profile"})
  591. addPrint(profNode)
  592. addRemove(profNode)
  593. profAdd := profNode.AddChild(&Node{Name: "add"})
  594. profAdd.AddChild(&Node{Name: "name="})
  595. profAdd.AddChild(&Node{Name: "local-address="})
  596. profAdd.AddChild(&Node{Name: "remote-address="})
  597. profAdd.AddChild(&Node{Name: "dns-server="})
  598. profAdd.AddChild(&Node{Name: "use-encryption="})
  599. profAdd.AddChild(&Node{Name: "only-one="})
  600. profAdd.AddChild(&Node{Name: "comment="})
  601. profSet := profNode.AddChild(&Node{Name: "set"})
  602. profSet.AddChild(&Node{Name: "local-address="})
  603. profSet.AddChild(&Node{Name: "remote-address="})
  604. profSet.AddChild(&Node{Name: "dns-server="})
  605. profSet.AddChild(&Node{Name: "use-encryption="})
  606. profSet.AddChild(&Node{Name: "only-one="})
  607. profSet.AddChild(&Node{Name: "comment="})
  608. // /ppp secret
  609. secretNode := pppNode.AddChild(&Node{Name: "secret"})
  610. addPrint(secretNode)
  611. addRemove(secretNode)
  612. addEnableDisable(secretNode)
  613. secAdd := secretNode.AddChild(&Node{Name: "add"})
  614. secAdd.AddChild(&Node{Name: "name="})
  615. secAdd.AddChild(&Node{Name: "password="})
  616. secAdd.AddChild(&Node{Name: "profile="})
  617. secAdd.AddChild(&Node{Name: "service="})
  618. secAdd.AddChild(&Node{Name: "local-address="})
  619. secAdd.AddChild(&Node{Name: "remote-address="})
  620. secAdd.AddChild(&Node{Name: "caller-id="})
  621. secAdd.AddChild(&Node{Name: "comment="})
  622. secSet := secretNode.AddChild(&Node{Name: "set"})
  623. secSet.AddChild(&Node{Name: "password="})
  624. secSet.AddChild(&Node{Name: "profile="})
  625. secSet.AddChild(&Node{Name: "service="})
  626. secSet.AddChild(&Node{Name: "local-address="})
  627. secSet.AddChild(&Node{Name: "remote-address="})
  628. secSet.AddChild(&Node{Name: "caller-id="})
  629. secSet.AddChild(&Node{Name: "comment="})
  630. secSet.AddChild(&Node{Name: "disabled="})
  631. // /ppp active
  632. actNode := pppNode.AddChild(&Node{Name: "active"})
  633. addPrint(actNode)
  634. addRemove(actNode)
  635. // 2. /ip
  636. ipNode := root.AddChild(&Node{Name: "ip"})
  637. // /ip address
  638. ipAddrNode := ipNode.AddChild(&Node{Name: "address", Aliases: []string{"addr"}})
  639. addPrint(ipAddrNode)
  640. addRemove(ipAddrNode)
  641. ipAddrAdd := ipAddrNode.AddChild(&Node{Name: "add"})
  642. ipAddrAdd.AddChild(&Node{Name: "address="})
  643. ipAddrAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  644. ipAddrAdd.AddChild(&Node{Name: "network="})
  645. // /ip route
  646. ipRouteNode := ipNode.AddChild(&Node{Name: "route", Aliases: []string{"routes"}})
  647. addPrint(ipRouteNode)
  648. addRemove(ipRouteNode)
  649. addEnableDisable(ipRouteNode)
  650. ipRouteAdd := ipRouteNode.AddChild(&Node{Name: "add"})
  651. ipRouteAdd.AddChild(&Node{Name: "dst-address="})
  652. ipRouteAdd.AddChild(&Node{Name: "gateway=", Dynamic: dynInterfaces})
  653. ipRouteAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  654. ipRouteAdd.AddChild(&Node{Name: "distance="})
  655. // /ip route rule
  656. ipRouteRule := ipRouteNode.AddChild(&Node{Name: "rule", Aliases: []string{"rules"}})
  657. addPrint(ipRouteRule)
  658. addRemove(ipRouteRule)
  659. ipRouteRuleAdd := ipRouteRule.AddChild(&Node{Name: "add"})
  660. ipRouteRuleAdd.AddChild(&Node{Name: "src-address="})
  661. ipRouteRuleAdd.AddChild(&Node{Name: "dst-address="})
  662. ipRouteRuleAdd.AddChild(&Node{Name: "table="})
  663. ipRouteRuleAdd.AddChild(&Node{Name: "action=", Dynamic: func() []string { return []string{"lookup", "unreachable", "drop"} }})
  664. // /ip route table
  665. ipRouteTable := ipRouteNode.AddChild(&Node{Name: "table", Aliases: []string{"tables"}})
  666. addPrint(ipRouteTable)
  667. addRemove(ipRouteTable)
  668. ipRouteTableAdd := ipRouteTable.AddChild(&Node{Name: "add"})
  669. ipRouteTableAdd.AddChild(&Node{Name: "name="})
  670. ipRouteTableAdd.AddChild(&Node{Name: "fib="})
  671. // /ip dhcp-client
  672. dhcpCliNode := ipNode.AddChild(&Node{Name: "dhcp-client"})
  673. addPrint(dhcpCliNode)
  674. addRemove(dhcpCliNode)
  675. dhcpCliNode.AddChild(&Node{Name: "renew"})
  676. dhcpCliNode.AddChild(&Node{Name: "release"})
  677. dhcpCliAdd := dhcpCliNode.AddChild(&Node{Name: "add"})
  678. dhcpCliAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  679. dhcpCliAdd.AddChild(&Node{Name: "disabled=", Dynamic: func() []string { return []string{"yes", "no"} }})
  680. // /ip dhcp-server
  681. dhcpSrvNode := ipNode.AddChild(&Node{Name: "dhcp-server"})
  682. addPrint(dhcpSrvNode)
  683. addRemove(dhcpSrvNode)
  684. addEnableDisable(dhcpSrvNode)
  685. dhcpSrvAdd := dhcpSrvNode.AddChild(&Node{Name: "add"})
  686. dhcpSrvAdd.AddChild(&Node{Name: "name="})
  687. dhcpSrvAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  688. dhcpSrvAdd.AddChild(&Node{Name: "address-pool="})
  689. dhcpSrvAdd.AddChild(&Node{Name: "lease-time="})
  690. // /ip dhcp-server lease
  691. dhcpLeaseNode := dhcpSrvNode.AddChild(&Node{Name: "lease", Aliases: []string{"leases"}})
  692. addPrint(dhcpLeaseNode)
  693. addRemove(dhcpLeaseNode)
  694. // /ip dhcp-server network
  695. dhcpNetNode := dhcpSrvNode.AddChild(&Node{Name: "network", Aliases: []string{"networks"}})
  696. addPrint(dhcpNetNode)
  697. addRemove(dhcpNetNode)
  698. dhcpNetAdd := dhcpNetNode.AddChild(&Node{Name: "add"})
  699. dhcpNetAdd.AddChild(&Node{Name: "address="})
  700. dhcpNetAdd.AddChild(&Node{Name: "gateway="})
  701. dhcpNetAdd.AddChild(&Node{Name: "dns-server="})
  702. // /ip dns
  703. dnsNode := ipNode.AddChild(&Node{Name: "dns"})
  704. addPrint(dnsNode)
  705. dnsSet := dnsNode.AddChild(&Node{Name: "set"})
  706. dnsSet.AddChild(&Node{Name: "servers="})
  707. dnsSet.AddChild(&Node{Name: "allow-remote-requests=", Dynamic: func() []string { return []string{"yes", "no"} }})
  708. dnsCache := dnsNode.AddChild(&Node{Name: "cache"})
  709. addPrint(dnsCache)
  710. dnsCache.AddChild(&Node{Name: "flush"})
  711. dnsStatic := dnsNode.AddChild(&Node{Name: "static"})
  712. addPrint(dnsStatic)
  713. addRemove(dnsStatic)
  714. dnsStaticAdd := dnsStatic.AddChild(&Node{Name: "add"})
  715. dnsStaticAdd.AddChild(&Node{Name: "name="})
  716. dnsStaticAdd.AddChild(&Node{Name: "address="})
  717. // /ip firewall
  718. fwNode := ipNode.AddChild(&Node{Name: "firewall"})
  719. // /ip firewall filter
  720. fwFilter := fwNode.AddChild(&Node{Name: "filter"})
  721. addPrint(fwFilter)
  722. addRemove(fwFilter)
  723. addEnableDisable(fwFilter)
  724. fwFilterAdd := fwFilter.AddChild(&Node{Name: "add"})
  725. fwFilterAdd.AddChild(&Node{Name: "chain=", Dynamic: func() []string { return []string{"forward", "input", "output"} }})
  726. fwFilterAdd.AddChild(&Node{Name: "action=", Dynamic: func() []string { return []string{"accept", "drop", "reject"} }})
  727. fwFilterAdd.AddChild(&Node{Name: "protocol=", Dynamic: func() []string { return []string{"icmp", "tcp", "udp"} }})
  728. fwFilterAdd.AddChild(&Node{Name: "src-address="})
  729. fwFilterAdd.AddChild(&Node{Name: "dst-address="})
  730. fwFilterAdd.AddChild(&Node{Name: "dst-port="})
  731. fwFilterAdd.AddChild(&Node{Name: "in-interface=", Dynamic: dynInterfaces})
  732. fwFilterAdd.AddChild(&Node{Name: "out-interface=", Dynamic: dynInterfaces})
  733. fwFilterAdd.AddChild(&Node{Name: "comment="})
  734. // /ip firewall nat
  735. fwNat := fwNode.AddChild(&Node{Name: "nat"})
  736. addPrint(fwNat)
  737. addRemove(fwNat)
  738. addEnableDisable(fwNat)
  739. fwNatAdd := fwNat.AddChild(&Node{Name: "add"})
  740. fwNatAdd.AddChild(&Node{Name: "chain=", Dynamic: func() []string { return []string{"dstnat", "srcnat"} }})
  741. fwNatAdd.AddChild(&Node{Name: "action=", Dynamic: func() []string { return []string{"accept", "dst-nat", "masquerade", "redirect", "src-nat"} }})
  742. fwNatAdd.AddChild(&Node{Name: "protocol=", Dynamic: func() []string { return []string{"icmp", "tcp", "udp"} }})
  743. fwNatAdd.AddChild(&Node{Name: "src-address="})
  744. fwNatAdd.AddChild(&Node{Name: "dst-address="})
  745. fwNatAdd.AddChild(&Node{Name: "to-addresses="})
  746. fwNatAdd.AddChild(&Node{Name: "to-ports="})
  747. fwNatAdd.AddChild(&Node{Name: "out-interface=", Dynamic: dynInterfaces})
  748. fwNatAdd.AddChild(&Node{Name: "comment="})
  749. // /ip firewall mangle
  750. fwMangle := fwNode.AddChild(&Node{Name: "mangle"})
  751. addPrint(fwMangle)
  752. addRemove(fwMangle)
  753. addEnableDisable(fwMangle)
  754. fwMangleAdd := fwMangle.AddChild(&Node{Name: "add"})
  755. fwMangleAdd.AddChild(&Node{Name: "chain=", Dynamic: func() []string { return []string{"forward", "input", "output", "postrouting", "prerouting"} }})
  756. fwMangleAdd.AddChild(&Node{Name: "action=", Dynamic: func() []string { return []string{"accept", "mark-packet", "mark-routing"} }})
  757. fwMangleAdd.AddChild(&Node{Name: "protocol=", Dynamic: func() []string { return []string{"icmp", "tcp", "udp"} }})
  758. fwMangleAdd.AddChild(&Node{Name: "src-address="})
  759. fwMangleAdd.AddChild(&Node{Name: "dst-address="})
  760. // /ip firewall address-list
  761. fwAddrList := fwNode.AddChild(&Node{Name: "address-list"})
  762. addPrint(fwAddrList)
  763. addRemove(fwAddrList)
  764. addEnableDisable(fwAddrList)
  765. fwAddrListAdd := fwAddrList.AddChild(&Node{Name: "add"})
  766. fwAddrListAdd.AddChild(&Node{Name: "list="})
  767. fwAddrListAdd.AddChild(&Node{Name: "address="})
  768. fwAddrListAdd.AddChild(&Node{Name: "comment="})
  769. // /ip firewall connection
  770. fwConn := fwNode.AddChild(&Node{Name: "connection", Aliases: []string{"connections"}})
  771. addPrint(fwConn)
  772. // /ip service
  773. serviceNode := ipNode.AddChild(&Node{Name: "service", Aliases: []string{"services"}})
  774. addPrint(serviceNode)
  775. serviceNode.AddChild(&Node{Name: "enable", Dynamic: dynServices})
  776. serviceNode.AddChild(&Node{Name: "disable", Dynamic: dynServices})
  777. serviceSet := serviceNode.AddChild(&Node{Name: "set", Dynamic: dynServices})
  778. serviceSet.AddChild(&Node{Name: "port="})
  779. serviceSet.AddChild(&Node{Name: "disabled=", Dynamic: func() []string { return []string{"yes", "no"} }})
  780. serviceSet.AddChild(&Node{Name: "certificate=", Dynamic: dynCertificates})
  781. // /ip sdwan
  782. sdwanNode := ipNode.AddChild(&Node{Name: "sdwan", Aliases: []string{"sd-wan"}})
  783. addPrint(sdwanNode)
  784. sdwanNode.AddChild(&Node{Name: "status"})
  785. addRemove(sdwanNode)
  786. addEnableDisable(sdwanNode)
  787. sdwanAdd := sdwanNode.AddChild(&Node{Name: "add"})
  788. sdwanAdd.AddChild(&Node{Name: "name="})
  789. sdwanAdd.AddChild(&Node{Name: "mode=", Dynamic: func() []string {
  790. return []string{"weighted", "pcc", "lowest-latency", "lowest-loss", "failover"}
  791. }})
  792. sdwanAdd.AddChild(&Node{Name: "auto-nat=", Dynamic: func() []string { return []string{"yes", "no"} }})
  793. sdwanAdd.AddChild(&Node{Name: "probe-interval="})
  794. sdwanAdd.AddChild(&Node{Name: "comment="})
  795. sdwanSet := sdwanNode.AddChild(&Node{Name: "set", Dynamic: dynSDWANGroups})
  796. sdwanSet.AddChild(&Node{Name: "mode=", Dynamic: func() []string {
  797. return []string{"weighted", "pcc", "lowest-latency", "lowest-loss", "failover"}
  798. }})
  799. sdwanSet.AddChild(&Node{Name: "auto-nat=", Dynamic: func() []string { return []string{"yes", "no"} }})
  800. sdwanSet.AddChild(&Node{Name: "probe-interval="})
  801. sdwanSet.AddChild(&Node{Name: "comment="})
  802. // /ip sdwan member
  803. sdwanMemNode := sdwanNode.AddChild(&Node{Name: "member", Aliases: []string{"members"}})
  804. addPrint(sdwanMemNode)
  805. addRemove(sdwanMemNode)
  806. addEnableDisable(sdwanMemNode)
  807. sdwanMemAdd := sdwanMemNode.AddChild(&Node{Name: "add"})
  808. sdwanMemAdd.AddChild(&Node{Name: "group=", Dynamic: dynSDWANGroups})
  809. sdwanMemAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  810. sdwanMemAdd.AddChild(&Node{Name: "gateway="})
  811. sdwanMemAdd.AddChild(&Node{Name: "weight="})
  812. sdwanMemAdd.AddChild(&Node{Name: "priority="})
  813. sdwanMemAdd.AddChild(&Node{Name: "target-probe="})
  814. sdwanMemAdd.AddChild(&Node{Name: "max-latency="})
  815. sdwanMemAdd.AddChild(&Node{Name: "max-loss="})
  816. sdwanMemAdd.AddChild(&Node{Name: "comment="})
  817. sdwanMemSet := sdwanMemNode.AddChild(&Node{Name: "set"})
  818. sdwanMemSet.AddChild(&Node{Name: "group=", Dynamic: dynSDWANGroups})
  819. sdwanMemSet.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  820. sdwanMemSet.AddChild(&Node{Name: "gateway="})
  821. sdwanMemSet.AddChild(&Node{Name: "weight="})
  822. sdwanMemSet.AddChild(&Node{Name: "priority="})
  823. sdwanMemSet.AddChild(&Node{Name: "target-probe="})
  824. sdwanMemSet.AddChild(&Node{Name: "max-latency="})
  825. sdwanMemSet.AddChild(&Node{Name: "max-loss="})
  826. sdwanMemSet.AddChild(&Node{Name: "comment="})
  827. // Also accessible directly at root /sdwan
  828. root.AddChild(sdwanNode)
  829. // 3. /routing
  830. routingNode := root.AddChild(&Node{Name: "routing"})
  831. // /routing bgp
  832. bgpNode := routingNode.AddChild(&Node{Name: "bgp"})
  833. bgpInst := bgpNode.AddChild(&Node{Name: "instance"})
  834. addPrint(bgpInst)
  835. bgpInstSet := bgpInst.AddChild(&Node{Name: "set"})
  836. bgpInstSet.AddChild(&Node{Name: "as="})
  837. bgpInstSet.AddChild(&Node{Name: "router-id="})
  838. bgpInstSet.AddChild(&Node{Name: "enabled=", Dynamic: func() []string { return []string{"yes", "no"} }})
  839. bgpPeer := bgpNode.AddChild(&Node{Name: "peer", Aliases: []string{"peers", "connection"}})
  840. addPrint(bgpPeer)
  841. addRemove(bgpPeer)
  842. addEnableDisable(bgpPeer)
  843. bgpPeerAdd := bgpPeer.AddChild(&Node{Name: "add"})
  844. bgpPeerAdd.AddChild(&Node{Name: "name="})
  845. bgpPeerAdd.AddChild(&Node{Name: "remote-address="})
  846. bgpPeerAdd.AddChild(&Node{Name: "remote-as="})
  847. bgpPeerAdd.AddChild(&Node{Name: "description="})
  848. bgpNet := bgpNode.AddChild(&Node{Name: "network"})
  849. addPrint(bgpNet)
  850. addRemove(bgpNet)
  851. bgpNetAdd := bgpNet.AddChild(&Node{Name: "add"})
  852. bgpNetAdd.AddChild(&Node{Name: "network="})
  853. bgpRoutes := bgpNode.AddChild(&Node{Name: "routes"})
  854. addPrint(bgpRoutes)
  855. // /routing ospf
  856. ospfNode := routingNode.AddChild(&Node{Name: "ospf"})
  857. ospfInst := ospfNode.AddChild(&Node{Name: "instance", Aliases: []string{"instances"}})
  858. addPrint(ospfInst)
  859. addRemove(ospfInst)
  860. ospfInstAdd := ospfInst.AddChild(&Node{Name: "add"})
  861. ospfInstAdd.AddChild(&Node{Name: "name="})
  862. ospfInstAdd.AddChild(&Node{Name: "router-id="})
  863. ospfInstAdd.AddChild(&Node{Name: "redistribute-connected=", Dynamic: func() []string { return []string{"yes", "no"} }})
  864. ospfInstAdd.AddChild(&Node{Name: "redistribute-static=", Dynamic: func() []string { return []string{"yes", "no"} }})
  865. ospfInstAdd.AddChild(&Node{Name: "redistribute-bgp=", Dynamic: func() []string { return []string{"yes", "no"} }})
  866. ospfInstAdd.AddChild(&Node{Name: "default-metric="})
  867. ospfInstSet := ospfInst.AddChild(&Node{Name: "set"})
  868. ospfInstSet.AddChild(&Node{Name: "router-id="})
  869. ospfInstSet.AddChild(&Node{Name: "redistribute-connected=", Dynamic: func() []string { return []string{"yes", "no"} }})
  870. ospfInstSet.AddChild(&Node{Name: "redistribute-static=", Dynamic: func() []string { return []string{"yes", "no"} }})
  871. ospfInstSet.AddChild(&Node{Name: "redistribute-bgp=", Dynamic: func() []string { return []string{"yes", "no"} }})
  872. ospfInstSet.AddChild(&Node{Name: "default-metric="})
  873. ospfInstSet.AddChild(&Node{Name: "disabled=", Dynamic: func() []string { return []string{"yes", "no"} }})
  874. ospfArea := ospfNode.AddChild(&Node{Name: "area", Aliases: []string{"areas"}})
  875. addPrint(ospfArea)
  876. addRemove(ospfArea)
  877. ospfAreaAdd := ospfArea.AddChild(&Node{Name: "add"})
  878. ospfAreaAdd.AddChild(&Node{Name: "name="})
  879. ospfAreaAdd.AddChild(&Node{Name: "area-id="})
  880. ospfAreaAdd.AddChild(&Node{Name: "instance="})
  881. ospfAreaAdd.AddChild(&Node{Name: "type=", Dynamic: func() []string { return []string{"default", "stub", "nssa"} }})
  882. ospfAreaAdd.AddChild(&Node{Name: "default-cost="})
  883. ospfAreaAdd.AddChild(&Node{Name: "no-summaries=", Dynamic: func() []string { return []string{"yes", "no"} }})
  884. ospfAreaRange := ospfNode.AddChild(&Node{Name: "area-range", Aliases: []string{"range", "ranges"}})
  885. addPrint(ospfAreaRange)
  886. addRemove(ospfAreaRange)
  887. ospfRangeAdd := ospfAreaRange.AddChild(&Node{Name: "add"})
  888. ospfRangeAdd.AddChild(&Node{Name: "prefix="})
  889. ospfRangeAdd.AddChild(&Node{Name: "area="})
  890. ospfRangeAdd.AddChild(&Node{Name: "cost="})
  891. ospfRangeAdd.AddChild(&Node{Name: "advertise=", Dynamic: func() []string { return []string{"yes", "no"} }})
  892. ospfIface := ospfNode.AddChild(&Node{Name: "interface", Aliases: []string{"interfaces", "template", "templates"}})
  893. addPrint(ospfIface)
  894. addRemove(ospfIface)
  895. ospfIfaceAdd := ospfIface.AddChild(&Node{Name: "add"})
  896. ospfIfaceAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  897. ospfIfaceAdd.AddChild(&Node{Name: "area="})
  898. ospfIfaceAdd.AddChild(&Node{Name: "networks="})
  899. ospfIfaceAdd.AddChild(&Node{Name: "cost="})
  900. ospfIfaceAdd.AddChild(&Node{Name: "priority="})
  901. ospfIfaceAdd.AddChild(&Node{Name: "network-type=", Dynamic: func() []string { return []string{"broadcast", "point-to-point", "nonbroadcast", "point-to-multipoint"} }})
  902. ospfIfaceAdd.AddChild(&Node{Name: "passive=", Dynamic: func() []string { return []string{"yes", "no"} }})
  903. ospfIfaceAdd.AddChild(&Node{Name: "auth=", Dynamic: func() []string { return []string{"none", "simple", "md5"} }})
  904. ospfSNbr := ospfNode.AddChild(&Node{Name: "static-neighbor", Aliases: []string{"static-neighbors"}})
  905. addPrint(ospfSNbr)
  906. addRemove(ospfSNbr)
  907. ospfSNbrAdd := ospfSNbr.AddChild(&Node{Name: "add"})
  908. ospfSNbrAdd.AddChild(&Node{Name: "address="})
  909. ospfSNbrAdd.AddChild(&Node{Name: "area="})
  910. ospfSNbrAdd.AddChild(&Node{Name: "poll-interval="})
  911. ospfSNbrAdd.AddChild(&Node{Name: "cost="})
  912. ospfNeighbor := ospfNode.AddChild(&Node{Name: "neighbor", Aliases: []string{"neighbors"}})
  913. addPrint(ospfNeighbor)
  914. ospfLSA := ospfNode.AddChild(&Node{Name: "lsa", Aliases: []string{"lsadb"}})
  915. addPrint(ospfLSA)
  916. ospfRoute := ospfNode.AddChild(&Node{Name: "route", Aliases: []string{"routes"}})
  917. addPrint(ospfRoute)
  918. // /routing filter
  919. routingFilter := routingNode.AddChild(&Node{Name: "filter"})
  920. addPrint(routingFilter)
  921. rfRule := routingFilter.AddChild(&Node{Name: "rule", Aliases: []string{"rules"}})
  922. addPrint(rfRule)
  923. addRemove(rfRule)
  924. rfRuleAdd := rfRule.AddChild(&Node{Name: "add"})
  925. rfRuleAdd.AddChild(&Node{Name: "chain="})
  926. rfRuleAdd.AddChild(&Node{Name: "action=", Dynamic: func() []string { return []string{"accept", "discard", "reject"} }})
  927. rfRuleAdd.AddChild(&Node{Name: "prefix="})
  928. rfRuleAdd.AddChild(&Node{Name: "prefix-len="})
  929. rfRuleAdd.AddChild(&Node{Name: "rule-order="})
  930. rfComm := routingFilter.AddChild(&Node{Name: "community-list", Aliases: []string{"community-lists"}})
  931. addPrint(rfComm)
  932. addRemove(rfComm)
  933. rfCommAdd := rfComm.AddChild(&Node{Name: "add"})
  934. rfCommAdd.AddChild(&Node{Name: "name="})
  935. rfCommAdd.AddChild(&Node{Name: "communities="})
  936. rfAS := routingFilter.AddChild(&Node{Name: "as-list", Aliases: []string{"as-lists", "num-list", "num-lists"}})
  937. addPrint(rfAS)
  938. addRemove(rfAS)
  939. rfASAdd := rfAS.AddChild(&Node{Name: "add"})
  940. rfASAdd.AddChild(&Node{Name: "name="})
  941. rfASAdd.AddChild(&Node{Name: "range="})
  942. // /routing rip
  943. ripNode := routingNode.AddChild(&Node{Name: "rip"})
  944. ripInst := ripNode.AddChild(&Node{Name: "instance", Aliases: []string{"instances"}})
  945. addPrint(ripInst)
  946. addRemove(ripInst)
  947. addEnableDisable(ripInst)
  948. ripInstAdd := ripInst.AddChild(&Node{Name: "add"})
  949. ripInstAdd.AddChild(&Node{Name: "name="})
  950. ripInstAdd.AddChild(&Node{Name: "version=", Dynamic: func() []string { return []string{"1", "2"} }})
  951. ripInstAdd.AddChild(&Node{Name: "redistribute-connected=", Dynamic: func() []string { return []string{"yes", "no"} }})
  952. ripInstAdd.AddChild(&Node{Name: "redistribute-static=", Dynamic: func() []string { return []string{"yes", "no"} }})
  953. ripInstAdd.AddChild(&Node{Name: "redistribute-ospf=", Dynamic: func() []string { return []string{"yes", "no"} }})
  954. ripInstAdd.AddChild(&Node{Name: "redistribute-bgp=", Dynamic: func() []string { return []string{"yes", "no"} }})
  955. ripInstAdd.AddChild(&Node{Name: "redistribute-default=", Dynamic: func() []string { return []string{"yes", "no"} }})
  956. ripIface := ripNode.AddChild(&Node{Name: "interface", Aliases: []string{"interfaces"}})
  957. addPrint(ripIface)
  958. addRemove(ripIface)
  959. addEnableDisable(ripIface)
  960. ripIfaceAdd := ripIface.AddChild(&Node{Name: "add"})
  961. ripIfaceAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  962. ripIfaceAdd.AddChild(&Node{Name: "instance="})
  963. ripIfaceAdd.AddChild(&Node{Name: "metric="})
  964. ripIfaceAdd.AddChild(&Node{Name: "mode=", Dynamic: func() []string { return []string{"multicast", "broadcast"} }})
  965. ripIfaceAdd.AddChild(&Node{Name: "passive=", Dynamic: func() []string { return []string{"yes", "no"} }})
  966. ripIfaceAdd.AddChild(&Node{Name: "use-bfd=", Dynamic: func() []string { return []string{"yes", "no"} }})
  967. ripNbr := ripNode.AddChild(&Node{Name: "neighbor", Aliases: []string{"neighbors"}})
  968. addPrint(ripNbr)
  969. addRemove(ripNbr)
  970. ripNbrAdd := ripNbr.AddChild(&Node{Name: "add"})
  971. ripNbrAdd.AddChild(&Node{Name: "address="})
  972. ripNbrAdd.AddChild(&Node{Name: "instance="})
  973. ripRoutes := ripNode.AddChild(&Node{Name: "routes", Aliases: []string{"route"}})
  974. addPrint(ripRoutes)
  975. // /routing bfd
  976. bfdNode := routingNode.AddChild(&Node{Name: "bfd"})
  977. bfdInst := bfdNode.AddChild(&Node{Name: "instance"})
  978. addPrint(bfdInst)
  979. bfdInstSet := bfdInst.AddChild(&Node{Name: "set"})
  980. bfdInstSet.AddChild(&Node{Name: "enabled=", Dynamic: func() []string { return []string{"yes", "no"} }})
  981. bfdIface := bfdNode.AddChild(&Node{Name: "interface", Aliases: []string{"interfaces"}})
  982. addPrint(bfdIface)
  983. addRemove(bfdIface)
  984. addEnableDisable(bfdIface)
  985. bfdIfaceAdd := bfdIface.AddChild(&Node{Name: "add"})
  986. bfdIfaceAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  987. bfdIfaceAdd.AddChild(&Node{Name: "min-rx="})
  988. bfdIfaceAdd.AddChild(&Node{Name: "min-tx="})
  989. bfdIfaceAdd.AddChild(&Node{Name: "idle-tx="})
  990. bfdIfaceAdd.AddChild(&Node{Name: "multiplier="})
  991. bfdIfaceAdd.AddChild(&Node{Name: "passive=", Dynamic: func() []string { return []string{"yes", "no"} }})
  992. bfdPeer := bfdNode.AddChild(&Node{Name: "peer", Aliases: []string{"peers", "neighbor", "neighbors"}})
  993. addPrint(bfdPeer)
  994. addRemove(bfdPeer)
  995. bfdPeerAdd := bfdPeer.AddChild(&Node{Name: "add"})
  996. bfdPeerAdd.AddChild(&Node{Name: "address="})
  997. bfdPeerAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  998. bfdPeerAdd.AddChild(&Node{Name: "multihop=", Dynamic: func() []string { return []string{"yes", "no"} }})
  999. bfdSession := bfdNode.AddChild(&Node{Name: "session", Aliases: []string{"sessions"}})
  1000. addPrint(bfdSession)
  1001. // /routing babel
  1002. babelNode := routingNode.AddChild(&Node{Name: "babel"})
  1003. babelInst := babelNode.AddChild(&Node{Name: "instance", Aliases: []string{"instances"}})
  1004. addPrint(babelInst)
  1005. addRemove(babelInst)
  1006. addEnableDisable(babelInst)
  1007. babelInstAdd := babelInst.AddChild(&Node{Name: "add"})
  1008. babelInstAdd.AddChild(&Node{Name: "name="})
  1009. babelInstAdd.AddChild(&Node{Name: "router-id="})
  1010. babelInstAdd.AddChild(&Node{Name: "redistribute-connected=", Dynamic: func() []string { return []string{"yes", "no"} }})
  1011. babelInstAdd.AddChild(&Node{Name: "redistribute-static=", Dynamic: func() []string { return []string{"yes", "no"} }})
  1012. babelInstAdd.AddChild(&Node{Name: "redistribute-ospf=", Dynamic: func() []string { return []string{"yes", "no"} }})
  1013. babelInstAdd.AddChild(&Node{Name: "redistribute-bgp=", Dynamic: func() []string { return []string{"yes", "no"} }})
  1014. babelIface := babelNode.AddChild(&Node{Name: "interface", Aliases: []string{"interfaces", "interface-template", "interface-templates"}})
  1015. addPrint(babelIface)
  1016. addRemove(babelIface)
  1017. addEnableDisable(babelIface)
  1018. babelIfaceAdd := babelIface.AddChild(&Node{Name: "add"})
  1019. babelIfaceAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  1020. babelIfaceAdd.AddChild(&Node{Name: "instance="})
  1021. babelIfaceAdd.AddChild(&Node{Name: "type=", Dynamic: func() []string { return []string{"wired", "wireless"} }})
  1022. babelIfaceAdd.AddChild(&Node{Name: "rxcost="})
  1023. babelIfaceAdd.AddChild(&Node{Name: "hello-interval="})
  1024. babelIfaceAdd.AddChild(&Node{Name: "update-interval="})
  1025. babelIfaceAdd.AddChild(&Node{Name: "auth=", Dynamic: func() []string { return []string{"none", "mac"} }})
  1026. babelIfaceAdd.AddChild(&Node{Name: "auth-key="})
  1027. babelIfaceLive := babelNode.AddChild(&Node{Name: "interface-live", Aliases: []string{"interfaces-live", "live"}})
  1028. addPrint(babelIfaceLive)
  1029. babelNbr := babelNode.AddChild(&Node{Name: "neighbor", Aliases: []string{"neighbors"}})
  1030. addPrint(babelNbr)
  1031. babelRoutes := babelNode.AddChild(&Node{Name: "routes", Aliases: []string{"route"}})
  1032. addPrint(babelRoutes)
  1033. // /routing radv
  1034. radvNode := routingNode.AddChild(&Node{Name: "radv"})
  1035. radvInst := radvNode.AddChild(&Node{Name: "instance", Aliases: []string{"instances"}})
  1036. addPrint(radvInst)
  1037. addRemove(radvInst)
  1038. addEnableDisable(radvInst)
  1039. radvInstAdd := radvInst.AddChild(&Node{Name: "add"})
  1040. radvInstAdd.AddChild(&Node{Name: "name="})
  1041. radvIface := radvNode.AddChild(&Node{Name: "interface", Aliases: []string{"interfaces"}})
  1042. addPrint(radvIface)
  1043. addRemove(radvIface)
  1044. addEnableDisable(radvIface)
  1045. radvIfaceAdd := radvIface.AddChild(&Node{Name: "add"})
  1046. radvIfaceAdd.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  1047. radvIfaceAdd.AddChild(&Node{Name: "instance="})
  1048. radvIfaceAdd.AddChild(&Node{Name: "min-ra-interval="})
  1049. radvIfaceAdd.AddChild(&Node{Name: "max-ra-interval="})
  1050. radvIfaceAdd.AddChild(&Node{Name: "default-lifetime="})
  1051. radvIfaceAdd.AddChild(&Node{Name: "prefixes="})
  1052. radvIfaceAdd.AddChild(&Node{Name: "rdnss="})
  1053. // 4. /tool
  1054. toolNode := root.AddChild(&Node{Name: "tool"})
  1055. toolPing := toolNode.AddChild(&Node{Name: "ping"})
  1056. toolPing.AddChild(&Node{Name: "count="})
  1057. toolPing.AddChild(&Node{Name: "size="})
  1058. toolPing.AddChild(&Node{Name: "interval="})
  1059. toolTrace := toolNode.AddChild(&Node{Name: "traceroute"})
  1060. toolTrace.AddChild(&Node{Name: "max-hops="})
  1061. toolBtest := toolNode.AddChild(&Node{Name: "bandwidth-test", Aliases: []string{"btest"}})
  1062. toolBtest.AddChild(&Node{Name: "target="})
  1063. toolBtest.AddChild(&Node{Name: "duration="})
  1064. toolBtest.AddChild(&Node{Name: "direction=", Dynamic: func() []string { return []string{"receive", "send", "both"} }})
  1065. toolNetwatch := toolNode.AddChild(&Node{Name: "netwatch"})
  1066. addPrint(toolNetwatch)
  1067. addRemove(toolNetwatch)
  1068. toolNWAdd := toolNetwatch.AddChild(&Node{Name: "add"})
  1069. toolNWAdd.AddChild(&Node{Name: "host="})
  1070. toolNWAdd.AddChild(&Node{Name: "interval="})
  1071. toolNWAdd.AddChild(&Node{Name: "timeout="})
  1072. toolIPScan := toolNode.AddChild(&Node{Name: "ip-scan"})
  1073. toolIPScan.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  1074. toolIPScan.AddChild(&Node{Name: "address-range="})
  1075. toolTorch := toolNode.AddChild(&Node{Name: "torch", Aliases: []string{"sniff"}})
  1076. toolTorch.AddChild(&Node{Name: "interface=", Dynamic: dynInterfaces})
  1077. // 5. /log
  1078. logNode := root.AddChild(&Node{Name: "log"})
  1079. logPrint := logNode.AddChild(&Node{Name: "print", Aliases: []string{"pr", "show"}})
  1080. logPrint.AddChild(&Node{Name: "limit="})
  1081. logPrint.AddChild(&Node{Name: "filter="})
  1082. // 6. /user
  1083. userNode := root.AddChild(&Node{Name: "user", Aliases: []string{"users"}})
  1084. addPrint(userNode)
  1085. addRemove(userNode)
  1086. userAdd := userNode.AddChild(&Node{Name: "add"})
  1087. userAdd.AddChild(&Node{Name: "name="})
  1088. userAdd.AddChild(&Node{Name: "password="})
  1089. userAdd.AddChild(&Node{Name: "role=", Dynamic: func() []string { return []string{"admin", "full", "read", "viewer"} }})
  1090. userAdd.AddChild(&Node{Name: "comment="})
  1091. userSet := userNode.AddChild(&Node{Name: "set"})
  1092. userSet.AddChild(&Node{Name: "name="})
  1093. userSet.AddChild(&Node{Name: "password="})
  1094. userSet.AddChild(&Node{Name: "role=", Dynamic: func() []string { return []string{"admin", "full", "read", "viewer"} }})
  1095. userSet.AddChild(&Node{Name: "comment="})
  1096. userSet.AddChild(&Node{Name: "disabled=", Dynamic: func() []string { return []string{"yes", "no"} }})
  1097. // 7. /system
  1098. sysNode := root.AddChild(&Node{Name: "system"})
  1099. sysRes := sysNode.AddChild(&Node{Name: "resource"})
  1100. addPrint(sysRes)
  1101. sysIdent := sysNode.AddChild(&Node{Name: "identity"})
  1102. addPrint(sysIdent)
  1103. sysIdentSet := sysIdent.AddChild(&Node{Name: "set"})
  1104. sysIdentSet.AddChild(&Node{Name: "name="})
  1105. sysPkg := sysNode.AddChild(&Node{Name: "package", Aliases: []string{"upgrade"}})
  1106. addPrint(sysPkg)
  1107. sysPkgUpdate := sysPkg.AddChild(&Node{Name: "update"})
  1108. sysPkgUpdate.AddChild(&Node{Name: "check"})
  1109. sysPkgUpdate.AddChild(&Node{Name: "install"})
  1110. sysNode.AddChild(&Node{Name: "reboot"})
  1111. sysNode.AddChild(&Node{Name: "shutdown", Aliases: []string{"poweroff"}})
  1112. // 8. /certificate
  1113. certNode := root.AddChild(&Node{Name: "certificate", Aliases: []string{"certificates", "cert"}})
  1114. addPrint(certNode)
  1115. certNode.AddChild(&Node{Name: "remove", Aliases: []string{"rem", "delete"}, Dynamic: dynCertificates})
  1116. certNode.AddChild(&Node{Name: "export", Dynamic: dynCertificates})
  1117. certAddCA := certNode.AddChild(&Node{Name: "add-ca"})
  1118. certAddCA.AddChild(&Node{Name: "name="})
  1119. certAddCA.AddChild(&Node{Name: "common-name="})
  1120. certAddCA.AddChild(&Node{Name: "days="})
  1121. certAddCA.AddChild(&Node{Name: "key-type=", Dynamic: func() []string { return []string{"RSA-2048", "RSA-4096", "ECDSA-P256"} }})
  1122. certAdd := certNode.AddChild(&Node{Name: "add"})
  1123. certAdd.AddChild(&Node{Name: "name="})
  1124. certAdd.AddChild(&Node{Name: "common-name="})
  1125. certAdd.AddChild(&Node{Name: "ca=", Dynamic: dynCAs})
  1126. certAdd.AddChild(&Node{Name: "sans="})
  1127. certAdd.AddChild(&Node{Name: "days="})
  1128. certAdd.AddChild(&Node{Name: "key-type=", Dynamic: func() []string { return []string{"RSA-2048", "RSA-4096", "ECDSA-P256"} }})
  1129. certLE := certNode.AddChild(&Node{Name: "issue-letsencrypt"})
  1130. certLE.AddChild(&Node{Name: "domain="})
  1131. certLE.AddChild(&Node{Name: "email="})
  1132. certLE.AddChild(&Node{Name: "name="})
  1133. certLE.AddChild(&Node{Name: "staging=", Dynamic: func() []string { return []string{"yes", "no"} }})
  1134. // 9. Direct root utility commands
  1135. root.AddChild(&Node{Name: "clear"})
  1136. root.AddChild(&Node{Name: "help", Aliases: []string{"?"}})
  1137. root.AddChild(&Node{Name: "quit"})
  1138. root.AddChild(&Node{Name: "exit"})
  1139. root.AddChild(&Node{Name: "ping"})
  1140. root.AddChild(&Node{Name: "traceroute"})
  1141. return root
  1142. }