package main import ( "encoding/json" "flag" "fmt" ) func cmdAP(args []string) error { if len(args) == 0 { return fmt.Errorf("ap: expected 'list' or 'get'") } c, err := NewClient() if err != nil { return err } switch args[0] { case "list", "ls": return cmdAPList(c, args[1:]) case "get": return cmdAPGet(c, args[1:]) default: return fmt.Errorf("ap: unknown verb %q (want: list, get)", args[0]) } } func cmdAPList(c *Client, args []string) error { o, _, err := parseListFlags("ap list", args) if err != nil { return err } items, p, raw, err := c.fetchList("/access_points", o) if err != nil { return err } return emitList(items, p, raw, o, func(items []json.RawMessage) error { tw := newTab() fmt.Fprintln(tw, "ID\tNAME\tTOWER\tIP_ADDRESS") for _, rm := range items { var a AccessPoint _ = json.Unmarshal(rm, &a) fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", a.ID, a.Name, a.Tower, a.IPAddress) } return tw.Flush() }) } func cmdAPGet(c *Client, args []string) error { jsonMode, id, err := parseGetFlags("ap get", args) if err != nil { return err } var a AccessPoint var raw []byte if err := c.get(apiPrefix+"/access_points/"+pathID(id), &a, &raw); err != nil { return err } if jsonMode { return printJSON(raw) } tw := newTab() fmt.Fprintln(tw, "ID\tNAME\tTOWER\tIP_ADDRESS") fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", a.ID, a.Name, a.Tower, a.IPAddress) return tw.Flush() } func cmdAPConfig(args []string) error { if len(args) == 0 || args[0] != "get" { return fmt.Errorf("ap-config: expected 'get '") } c, err := NewClient() if err != nil { return err } jsonMode, id, err := parseGetFlags("ap-config get", args[1:]) if err != nil { return err } var cfg AccessPointConfig var raw []byte if err := c.get(apiPrefix+"/access_points/"+pathID(id)+"/config", &cfg, &raw); err != nil { return err } if jsonMode { return printJSON(raw) } // Config is heterogeneous; pretty-print key fields and dump JSON. tw := newTab() if cfg.ShaperRate != nil { fmt.Fprintf(tw, "shaper.up_speed\t%s\n", fmtSpeed(cfg.ShaperRate.UpSpeed)) fmt.Fprintf(tw, "shaper.down_speed\t%s\n", fmtSpeed(cfg.ShaperRate.DownSpeed)) fmt.Fprintf(tw, "shaper.activate\t%v\n", cfg.ShaperRate.Activate) } if cfg.SNMPCommunity != "" { fmt.Fprintf(tw, "snmp_community\t%s\n", cfg.SNMPCommunity) } if cfg.SNMP3 != nil { fmt.Fprintf(tw, "snmp3.security_name\t%s\n", cfg.SNMP3.SecurityName) fmt.Fprintf(tw, "snmp3.auth_protocol\t%s\n", cfg.SNMP3.AuthProtocol) fmt.Fprintf(tw, "snmp3.priv_protocol\t%s\n", cfg.SNMP3.PrivProtocol) } return tw.Flush() } // Score endpoints don't paginate (per spec, single response body). func cmdAPScores(args []string) error { fs := flag.NewFlagSet("ap-scores", flag.ExitOnError) jsonMode := fs.Bool("json", false, "emit raw API JSON") if err := fs.Parse(args); err != nil { return err } c, err := NewClient() if err != nil { return err } var raw []byte if err := c.get(apiPrefix+"/access_point_scores", nil, &raw); err != nil { return err } if *jsonMode { return printJSON(raw) } // Spec is ambiguous; try array, fall back to single object. var arr []AccessPointScore if err := json.Unmarshal(raw, &arr); err != nil { var one AccessPointScore if err2 := json.Unmarshal(raw, &one); err2 == nil { arr = []AccessPointScore{one} } else { return fmt.Errorf("decode ap-scores: %w (body: %s)", err, truncate(string(raw), 200)) } } tw := newTab() fmt.Fprintln(tw, "NAME\tSTATUS\tSITE\tRF\tHEALTH\tCONN\tLATENCY_MS") for _, s := range arr { fmt.Fprintf(tw, "%s\t%s\t%s\t%.1f\t%.1f\t%d\t%.1f\n", s.Name, s.Status, s.SiteName, s.RFScore, s.APHealthScore, int(s.Connections), s.Latency) } return tw.Flush() }