// getFwCmds return commands for firewall subsection func getSpamCmds(client *govh.OvhClient) (spamCmds []cli.Command) { ipr, err := ip.New(client) if err != nil { return } spamCmds = []cli.Command{ { Name: "listIp", Usage: "List IP which send (or have sent) spam.", Description: "ovh spam listIp IPBLOCK [--state ]" + NLTAB + "Example: ovh spam listIp 91.121.228.135/32 --state unblocked", Flags: []cli.Flag{ cli.StringFlag{"state", "", "The state of the IP (blockedForSpam|unblocked|unblocking).", ""}, }, Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 1) var state string if c.IsSet("state") { state = c.String("state") if !inSliceStr(state, []string{"blockedForSpam", "unblocked", "unblocking"}) { dieBadArgs() } } ips, err := ipr.SpamGetSpammingIps(ip.IpBlock{c.Args().First(), ""}, state) handleErrFromOvh(err) for _, ip := range ips { fmt.Println(ip) } dieOk() }, }, { Name: "getProperties", Usage: "Get properties of a spamming IP.", Description: "ovh spam getProperties IPBLOCK IP" + NLTAB + "Example: ovh spam listIp 91.121.228.135/32 91.121.228.135", Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 2) p, err := ipr.SpamGetIp(ip.IpBlock{c.Args().First(), ""}, c.Args().Get(1)) handleErrFromOvh(err) dieOk(fmt.Sprintf("Blocked since (duration sec):%d%sLast time: %s%sIP: %s%sState: %s", p.Time, NL, p.Date, NL, p.IpSpamming, NL, p.State)) }, }, { Name: "getStats", Usage: "Get spam stats about a spamming IP.", Description: "ovh spam getStats IPBLOCK IP --from TIMESTAMP_START --to TIMESTAMP_STOP" + NLTAB + "Example: ovh spam getStats 178.33.223.32/28 178.33.223.42 --from 1385251200 --to 1387882630", Flags: []cli.Flag{ cli.StringFlag{"from", "", "Unix timestamp representing the begining of the peiod (required).", ""}, cli.StringFlag{"to", "", "Unix timestamp representing the end of the peiod (required).", ""}, }, Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 2) if !c.IsSet("from") || !c.IsSet("to") { dieBadArgs() } from := c.Int("from") to := c.Int("to") if from >= to { dieBadArgs() } stats, err := ipr.SpamGetIpStats(ip.IpBlock{c.Args().First(), ""}, c.Args().Get(1), time.Unix(int64(from), 0), time.Unix(int64(to), 0)) handleErrFromOvh(err) if stats == nil { dieOk("No spam stats for this period") } fmt.Printf("Blocked for the last time: %s%s", time.Unix(stats.Timestamp, 0).Format(time.RFC822Z), NL) fmt.Printf("Number of emails sent: %d%s", stats.Total, NL) fmt.Printf("Number of spams sent: %d%s", stats.NumberOfSpams, NL) fmt.Printf("Average score: %d%s%s", stats.AverageSpamScore, NL, NL) if len(stats.DetectedSpams) > 0 { fmt.Println("Detected Spams : ", NL) } for _, ds := range stats.DetectedSpams { fmt.Println("") fmt.Printf("\tDate: %s%s", time.Unix(ds.Date, 0).Format(time.RFC822Z), NL) fmt.Printf("\tMessage ID: %s%s", ds.MessageId, NL) fmt.Printf("\tDestination IP: %s%s", ds.DestinationIp, NL) fmt.Printf("\tScore: %d%s", ds.Spamscore, NL) } dieOk() }, }, { Name: "unblock", Usage: "Unblock a locked IP.", Description: "ovh spam unblock IPBLOCK IP" + NLTAB + "Example: ovh spam unblock 91.121.228.135/32 91.121.228.135", Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 2) handleErrFromOvh(ipr.SpamUnblockSpamIp(ip.IpBlock{c.Args().First(), ""}, c.Args().Get(1))) dieDone() }, }, { Name: "getBlocked", Usage: "Retuns IPs which are currently blocked.", Description: "ovh spam getBlocked" + NLTAB + "Example: ovh spam getBlocked", Action: func(c *cli.Context) { //dieIfArgsMiss(len(c.Args()), 2) ips, err := ipr.GetBlockedForSpam() handleErrFromOvh(err) for _, ip := range ips { fmt.Println(ip) } dieOk() }, }, } return }
// getIpCmds return commands for Ip section func getIpCmds(client *govh.OvhClient) (ipCmds []cli.Command) { ipr, err := ip.New(client) if err != nil { return } // Ip commands ipCmds = []cli.Command{ // list { Name: "list", Usage: "List your IP blocks.", Description: "ovh ip list [flag...]" + NLTAB + "Example: ovh ip list --type vps", Flags: []cli.Flag{ cli.StringFlag{"desc", "", "Filter: by description (like).", ""}, cli.StringFlag{"ip", "", "Filter: by IP (contains or equals).", ""}, cli.StringFlag{"routedTo", "", "Filter: by routing.", ""}, cli.StringFlag{"type", "all", "Filter: by IP block type: all|cdn|dedicated|failover|hosted_ssl|housing|loadBalancing|mail|pcc|pci|private|vps|vpn|vrack|xdsl", ""}, }, Action: func(c *cli.Context) { fDesc := strings.ToLower(c.String("desc")) fIp := strings.ToLower(c.String("ip")) fRoutedTo := strings.ToLower(c.String("routedto")) fType := strings.ToLower(c.String("type")) if fType == "all" { fType = "" } ips, err := ipr.List(fDesc, fIp, fRoutedTo, fType) handleErrFromOvh(err) for _, i := range ips { fmt.Println(i.IP, i.Type) } dieOk() }, }, // getProperties { Name: "getProperties", Usage: "Get properties of an IP.", Description: "ovh ip getProperties IPBLOCK" + NLTAB + "Example: ovh ip getProperties 91.121.228.135/32", Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 1) properties, err := ipr.GetIpProperties(c.Args().First()) handleErrFromOvh(err) dieOk(fmt.Sprintf("IP: %s%sType: %s%sDescription: %s%sRouted to: %s", properties.Ip, NL, properties.Type, NL, properties.Description, NL, properties.RoutedTo.ServiceName)) }, }, // Update properties { Name: "updateProperties", Usage: "Update properties of an IP", Description: `ovh ip updateProperties IPBLOCK --desc "description"` + NLTAB + `Example: ovh ip updateProperties 37.187.0.144/32 --desc "IP routed to lunar base server"`, Flags: []cli.Flag{ cli.StringFlag{"desc", "", "Update description", ""}, }, Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 1) fDesc := c.String("desc") // check if there is something to update if len(fDesc) == 0 { dieDone() } err := ipr.UpdateProperties(c.Args().First(), fDesc) if err != nil { dieError(err) } dieDone() }, }, } return }
func ipHandler(cmd *Cmd) (err error) { // New govh client client := govh.NewClient(OVH_APP_KEY, OVH_APP_SECRET, ck) // New ip ressource ipr, err := ip.New(client) if err != nil { return } var resp string switch cmd.Action { // List case "list": ipType := "all" if len(cmd.Args) > 2 { ipType = cmd.Args[2] } ips, err := ipr.List(ipType) if err != nil { dieError(err) } for _, i := range ips { resp = fmt.Sprintf("%s%s\r\n", resp, i.IP) } if len(resp) > 2 { resp = resp[0 : len(resp)-2] } dieOk(resp) break case "lb": if len(cmd.Args) < 3 { dieError("\"ip lb\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md") } var t []byte t, err = ipr.LbList() resp = string(t) dieOk(resp) break case "fw": // ip fw ipBlock.IP list // ip fw x.x.x.x/y // Return IP V4 list of this block which is under firewall if len(cmd.Args) == 4 && cmd.Args[3] == "list" { block := ip.IpBlock{cmd.Args[2], ""} ips, err := ipr.FwListIpOfBlock(block) if err != nil { dieError(err) } for _, i := range ips { resp = fmt.Sprintf("%s%s\r\n", resp, i) } if len(resp) > 2 { resp = resp[0 : len(resp)-2] } dieOk(resp) break } // Add IP to firewall // cmd : ip fw ibBlock.IP ipV4 add if len(cmd.Args) == 5 && cmd.Args[4] == "add" { block := ip.IpBlock{cmd.Args[2], ""} if err = ipr.FwAddIp(block, cmd.Args[3]); err != nil { dieError(err) } dieOk(fmt.Sprintf("%s added to firewall", cmd.Args[3])) } // Get properties of a firewalled IP // ip fw ipBlock.IP ipV4 prop if len(cmd.Args) == 5 && cmd.Args[4] == "prop" { block := ip.IpBlock{cmd.Args[2], ""} i, err := ipr.FwGetIpProperties(block, cmd.Args[3]) if err != nil { dieError(err) } dieOk(fmt.Sprintf("ipOnFirewall: %s%sEnabled: %t%sState: %s", i.IpOnFirewall, NL, i.Enabled, NL, i.State)) break } // Enable firewalll for IP ipv4 // ip fw ipVlock ipV4 enable if len(cmd.Args) == 5 && cmd.Args[4] == "enable" { block := ip.IpBlock{cmd.Args[2], ""} err := ipr.FwSetFirewallEnable(block, cmd.Args[3], true) if err != nil { dieError(err) } dieOk("ok") break } // Disable firewalll for IP ipv4 // ip fw ipVlock ipV4 disable if len(cmd.Args) == 5 && cmd.Args[4] == "disable" { block := ip.IpBlock{cmd.Args[2], ""} err := ipr.FwSetFirewallEnable(block, cmd.Args[3], false) if err != nil { dieError(err) } dieOk("ok") break } // Remove IPv4 from firewall // cmd : ip fw ipBlock.IP ipV4 remove if len(cmd.Args) == 5 && cmd.Args[4] == "remove" { block := ip.IpBlock{cmd.Args[2], ""} if err = ipr.FwRemoveIp(block, cmd.Args[3]); err != nil { dieError(err) } dieOk(fmt.Sprintf("%s removed from firewall", cmd.Args[3])) } // Get rules sequences // ip fw ipBlock.IP ipV4 listRules all if len(cmd.Args) >= 5 && cmd.Args[4] == "listRules" { block := ip.IpBlock{cmd.Args[2], ""} state := "" if len(cmd.Args) == 6 { state = cmd.Args[6] } t, err := ipr.FwGetRulesSequences(block, cmd.Args[3], state) if err != nil { dieError(err) } var r string if len(t) > 0 { r = fmt.Sprintf("%d", t[0]) for _, s := range t[1:] { r = fmt.Sprintf("%s%s%d", r, NL, s) } } dieOk(r) } // Add rule // ip fw ipBlock.IP ipV4 addRule rule (as Json) if len(cmd.Args) == 6 && cmd.Args[4] == "addRule" { block := ip.IpBlock{cmd.Args[2], ""} // Check json var rule ip.FirewallRule2Add err := json.Unmarshal([]byte(cmd.Args[5]), &rule) if err != nil { dieError("Rule error. See doc at : https://github.com/Toorop/ovh-cli", err) } err = ipr.FwAddRule(block, cmd.Args[3], rule) if err != nil { dieError(err) } dieOk("OK") } // Remove rule // ip fw ipBlock.IP ipV4 remRule ruleSequence if len(cmd.Args) == 6 && cmd.Args[4] == "remRule" { block := ip.IpBlock{cmd.Args[2], ""} sequence, err := strconv.Atoi(cmd.Args[5]) if err != nil { dieError(err) } err = ipr.FwRemoveRule(block, cmd.Args[3], sequence) if err != nil { dieError(err) } dieOk(fmt.Sprintf("Rule %d removed", sequence)) } // Get rule // ip fw ipBlock.IP ipV4 getRule sequence if len(cmd.Args) == 6 && cmd.Args[4] == "getRule" { block := ip.IpBlock{cmd.Args[2], ""} sequence, err := strconv.Atoi(cmd.Args[5]) if err != nil { dieError(err) } rule, err := ipr.FwGetRule(block, cmd.Args[3], sequence) if err != nil { dieError(err) } out := "" if len(rule.Protocol) > 0 { out = fmt.Sprintf("%sProtocol: %s%s", out, rule.Protocol, NL) } if len(rule.Source) > 0 { out = fmt.Sprintf("%sSource: %s%s", out, rule.Source, NL) } if len(rule.DestinationPort) > 0 { out = fmt.Sprintf("%sDestinationPort: %s%s", out, rule.DestinationPort, NL) } out = fmt.Sprintf("%sSequence: %d%s", out, rule.Sequence, NL) if len(rule.Options) > 0 { out = fmt.Sprintf("%sOptions: %s%s", out, strings.Join(rule.Options, " "), NL) } if len(rule.Destination) > 0 { out = fmt.Sprintf("%sDestination: %s%s", out, rule.Destination, NL) } if len(rule.Rule) > 0 { out = fmt.Sprintf("%sRule: %s%s", out, rule.Rule, NL) } if len(rule.SourcePort) > 0 { out = fmt.Sprintf("%sSourcePort: %s%s", out, rule.SourcePort, NL) } if len(rule.State) > 0 { out = fmt.Sprintf("%sState: %s%s", out, rule.State, NL) } if len(rule.CreationDate) > 0 { out = fmt.Sprintf("%sCreationDate: %s%s", out, rule.CreationDate, NL) } if len(rule.Action) > 0 { out = fmt.Sprintf("%sAction: %s%s", out, rule.Action, NL) } dieOk(out[0 : len(out)-2]) } err = errors.New(fmt.Sprintf("This action : '%s' is not valid or not implemented yet !", strings.Join(cmd.Args, " "))) break case "spam": // List of spamming IP // ip spam ipBlock.IP listSpammingIp STATE if len(cmd.Args) >= 4 && cmd.Args[3] == "listSpammingIp" { block := ip.IpBlock{cmd.Args[2], ""} state := "" if len(cmd.Args) == 5 { state = cmd.Args[4] } ips, err := ipr.SpamGetSpammingIps(block, state) if err != nil { dieError(err) } for _, ip := range ips { fmt.Println(ip) } dieOk("") } // detailed info about a spamming IP // ip spam ipBlock.IP ipv4 details if len(cmd.Args) == 5 && cmd.Args[4] == "details" { block := ip.IpBlock{cmd.Args[2], ""} spamIp, err := ipr.SpamGetSpamIp(block, cmd.Args[3]) if err != nil { dieError(err) } dieOk(fmt.Sprintf("Time: %d%sDate: %s%sIpSpamming: %s%sState: %s", spamIp.Time, NL, spamIp.Date, NL, spamIp.IpSpamming, NL, spamIp.State)) } // Stats about a spamming IP // ip spam ipBlock.IP ipv4 stats FROM TO if len(cmd.Args) == 7 && cmd.Args[4] == "stats" { block := ip.IpBlock{cmd.Args[2], ""} from, err := strconv.ParseInt(cmd.Args[5], 10, 64) if err != nil { dieError(err) } to, err := strconv.ParseInt(cmd.Args[6], 10, 64) if err != nil { dieError(err) } spamStats, err := ipr.SpamGetIpStats(block, cmd.Args[3], time.Unix(from, 0), time.Unix(to, 0)) if err != nil { dieError(err) } if spamStats == nil { dieOk("No spam stats for this period") } fmt.Printf("Blocked for the last time: %s%s", time.Unix(spamStats.Timestamp, 0).Format(time.RFC822Z), NL) fmt.Printf("Number of emails sent: %d%s", spamStats.Total, NL) fmt.Printf("Number of spams sent: %d%s", spamStats.NumberOfSpams, NL) fmt.Printf("Average score: %d%s%s", spamStats.AverageSpamScore, NL, NL) if len(spamStats.DetectedSpams) > 0 { fmt.Println("Detected Spams : ", NL) } for _, ds := range spamStats.DetectedSpams { fmt.Println("") fmt.Printf("\tDate: %s%s", time.Unix(ds.Date, 0).Format(time.RFC822Z), NL) fmt.Printf("\tMessage ID: %s%s", ds.MessageId, NL) fmt.Printf("\tDestination IP: %s%s", ds.DestinationIp, NL) fmt.Printf("\tScore: %d%s", ds.Spamscore, NL) } dieOk("") } // Unblock // ip spam ipBlock.IP ipv4 unblock if len(cmd.Args) == 5 && cmd.Args[4] == "unblock" { block := ip.IpBlock{cmd.Args[2], ""} err := ipr.SpamUnblockSpamIp(block, cmd.Args[3]) if err != nil { dieError(err) } dieOk("ok") } err = errors.New(fmt.Sprintf("This action : '%s' is not valid or not implemented yet !", strings.Join(cmd.Args, " "))) break case "getBlockedForSpam": // On va chercher les blocks ips, err := ipr.GetBlockedForSpam() if err != nil { dieError(err) } if len(ips) == 0 { dieOk("") } for _, i := range ips { fmt.Println(i) } dieOk("") // On les tests break default: err = errors.New(fmt.Sprintf("This action : '%s' is not valid or not implemented yet !", strings.Join(cmd.Args, " "))) } return }
// getFwCmds return commands for firewall subsection func getFwCmds(client *govh.OvhClient) (fwCmds []cli.Command) { ipr, err := ip.New(client) if err != nil { return } fwCmds = []cli.Command{ { Name: "list", Usage: "List IPs, of a given block, that are under firewall.", Description: "ovh fw list IPBLOCK" + NLTAB + "Example: ovh fw list 91.121.228.135/32 ", Action: func(c *cli.Context) { if len(c.Args()) == 0 { dieBadArgs() } ips, err := ipr.FwListIpOfBlock(ip.IpBlock{c.Args().First(), ""}) handleErrFromOvh(err) for _, ip := range ips { fmt.Println(ip) } dieOk() }, }, { Name: "add", Usage: "Add an IP of IPBLOCK on firewall.", Description: "ovh fw add IPBLOCK IP" + NLTAB + "Example: ovh fw add 92.222.14.249/32 92.222.14.249", Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 2) err := ipr.FwAddIp(ip.IpBlock{c.Args().First(), ""}, c.Args().Get(1)) handleErrFromOvh(err) dieDone() }, }, { Name: "remove", Usage: "Remove an IP of IPBLOCK from firewall.", Description: "ovh fw remove IPBLOCK IP" + NLTAB + "Example: ovh fw remove 92.222.14.249/32 92.222.14.249", Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 2) err := ipr.FwRemoveIp(ip.IpBlock{c.Args().First(), ""}, c.Args().Get(1)) handleErrFromOvh(err) dieDone() }, }, { Name: "getProperties", Usage: "Get properties of an IP on the firewall.", Description: "ovh fw getProperties IPBLOCK IP " + NLTAB + "Example: ovh fw getProperties 92.222.14.249/32 92.222.14.249", Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 2) p, err := ipr.FwGetIpProperties(ip.IpBlock{c.Args().First(), ""}, c.Args().Get(1)) handleErrFromOvh(err) dieOk(fmt.Sprintf("Ip: %s%sEnabled: %t%sState: %s", p.Ip, NL, p.Enabled, NL, p.State)) }, }, { Name: "update", Usage: "Update an IP on the firewall.", Description: "ovh fw update IPBLOCK IP [--flag...]" + NLTAB + "Example: ovh fw update 92.222.14.249/32 92.222.14.249 --enable true", Flags: []cli.Flag{ cli.StringFlag{"enabled", "", "Set enabled state of the IP (true|false).", ""}, }, Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 2) fEnabled := c.Bool("enabled") err := ipr.FwUpdateIp(ip.IpBlock{c.Args().First(), ""}, c.Args().Get(1), fEnabled) handleErrFromOvh(err) dieDone() }, }, { Name: "listRules", Usage: "Return a list ao rule sequences.", Description: "ovh fw listRule IPBLOCK IP [--state]" + NLTAB + "Example: ovh fw listRule 92.222.14.249/32 92.222.14.249 --state ok", Flags: []cli.Flag{ cli.StringFlag{"state", "", "Filter on state (creationPending|ok|removalPending).", ""}, }, Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 2) var sequences []int if c.IsSet("state") { fState := c.String("state") sequences, err = ipr.FwListRules(ip.IpBlock{c.Args().First(), ""}, c.Args().Get(1), fState) } else { sequences, err = ipr.FwListRules(ip.IpBlock{c.Args().First(), ""}, c.Args().Get(1)) } handleErrFromOvh(err) for _, seq := range sequences { fmt.Println(seq) } dieOk() }, }, { Name: "addRule", Usage: "Add a new rule on an IP.", Description: "ovh fw addRule [--flag...] IPBLOCK IP " + NLTAB + "Example: ovh fw addRule --action deny --protocol tcp --toPort 22 --sequence 0 92.222.14.249/32 92.222.14.249", Flags: []cli.Flag{ cli.StringFlag{"action", "", "Action on this rule (deny|permit). Required.", ""}, cli.StringFlag{"sequence", "", "Sequence number of your rule. Required.", ""}, cli.StringFlag{"protocol", "", "Network protocol (ah|esp|gre|icmp|ipv4|tcp|udp). Requiered.", ""}, cli.StringFlag{"fromPort", "", "Source port for your rule. Only with TCP/UDP protocol", ""}, cli.StringFlag{"fromIp", "", "Source ip for your rule. Any if not set.", ""}, cli.StringFlag{"toPort", "", "Destination port for your rule. Only with TCP/UDP protocol.", ""}, cli.StringFlag{"tcpFragments", "", "Can only be used with TCP protocol (true|false)", ""}, cli.StringFlag{"tcpOption", "", "Can only be used with TCP protocol (established|syn)", ""}, }, Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 2) rule := ip.FwRule2Add{} // action if !c.IsSet("action") { dieBadArgs() } action := strings.ToLower(c.String("action")) if !inSliceStr(action, []string{"deny", "permit"}) { dieBadArgs() } rule.Action = action // sequence if !c.IsSet("sequence") { dieBadArgs() } sequence := c.Int("sequence") rule.Sequence = sequence // protocol if !c.IsSet("protocol") { dieBadArgs() } protocol := strings.ToLower(c.String("protocol")) if !inSliceStr(protocol, []string{"ah", "esp", "gre", "icmp", "ipv4", "tcp", "udp"}) { dieBadArgs() } rule.Protocol = protocol // fromPort if c.IsSet("fromPort") { rule.FromPort = c.Int("fromPort") } // fromIP if c.IsSet("fromIp") { rule.FromIp = c.String("fromIp") } // toPort if c.IsSet("toPort") { rule.ToPort = c.Int("toPort") } // fwTcpOption fwTcpOption := ip.FwTcpOption{} flagFwTcpOption := false // tcpOptionFragment if c.IsSet("tcpFragments") { fwTcpOption.Fragments = c.Bool("tcpFragments") flagFwTcpOption = true } // tcpOption if c.IsSet("tcpOption") { tcpOption := c.String("tcpOption") if !inSliceStr(tcpOption, []string{"established", "syn"}) { dieBadArgs() } fwTcpOption.Option = tcpOption flagFwTcpOption = true } if flagFwTcpOption { rule.TcpOption = &fwTcpOption } handleErrFromOvh(ipr.FwAddRule(ip.IpBlock{c.Args().First(), ""}, c.Args().Get(1), rule)) dieDone() }, }, { Name: "removeRule", Usage: "Remove a firwall rule.", Description: "ovh fw removeRule IPBLOCK IP SEQUENCE" + NLTAB + "Example: ovh fw removeRule 92.222.14.249/32 92.222.14.249 0", Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 3) sequence, err := strconv.ParseInt(c.Args().Get(2), 10, 16) if err != nil { dieError(err) } handleErrFromOvh(ipr.FwRemoveRule(ip.IpBlock{c.Args().First(), ""}, c.Args().Get(1), int(sequence))) dieDone() }, }, { Name: "getRuleProperties", Usage: "Get properties of firewall rule.", Description: "ovh fw getRuleProperties IPBLOCK IP SEQUENCE " + NLTAB + "Example: ovh fw getRuleProperties 92.222.14.249/32 92.222.14.249 0", Action: func(c *cli.Context) { dieIfArgsMiss(len(c.Args()), 3) sequence, err := strconv.ParseInt(c.Args().Get(2), 10, 16) if err != nil { dieError(err) } p, err := ipr.FwGetRuleProperties(ip.IpBlock{c.Args().First(), ""}, c.Args().Get(1), int(sequence)) handleErrFromOvh(err) dieOk(fmt.Sprintf("Sequence: %d%sCreated: %s%sProtocol: %s%sFromIp: %s%sFromPort: %s%sToIP: %s%sToPort: %s%sAction: %s%sRule:%s%sState: %s%sTcpOption: %s%sFragments: %t", p.Sequence, NL, p.CreationDate, NL, p.Protocol, NL, p.FromIp, NL, p.FromPort, NL, p.ToIp, NL, p.ToPort, NL, p.Action, NL, p.Rule, NL, p.State, NL, p.TcpOption, NL, p.Fragments)) }, }, } return }