package lib import ( "errors" "fmt" "net" "regexp" "strconv" "strings" "github.rackspace.com/gorax/adx/lib/parsers" "github.rackspace.com/gorax/clients/sshAPI" "github.rackspace.com/gorax/tools/util" ) // IsActive method will return a boolean true/false depending if the // unit is the current active/master unit. func (adx *ADX) IsActive() (bool, error) { err := adx.GetVrrpE() if err != nil { return false, err } if !adx.Status.VrrpE.Enabled { return true, nil } state := "" for _, i := range adx.Status.VrrpE.Int { if state == "" { state = i.State continue } if state != i.State { return false, errors.New("state mismatch") } } if state == "master" { return true, nil } return false, nil } func (adx *ADX) EthernetNetwork(eth int) *net.IPNet { return parsers.EthernetNetwork(eth, adx.Config.EthInterfaces) } func (adx *ADX) ManagementMAC(i int) string { return parsers.InterfaceMAC("mgmt", i, adx.Status.Interfaces) } func (adx *ADX) EthernetMAC(i int) string { return parsers.InterfaceMAC("Ethernet", i, adx.Status.Interfaces) } func (adx *ADX) VrrpeMAC(i int) *string { return parsers.VrrpEMAC(i, adx.Status.VrrpE.Int) } // TFTPToDisk method will run the commands to copy a file to disk using TFTP func (adx *ADX) TFTPToDisk(source, filename, location string, timeout int) (string, error) { var ( respAny = regexp.MustCompile(`(?i).*\n(.*)\n`) respErr = regexp.MustCompile(`(?i)(.*Error.*)\n`) respSuc = regexp.MustCompile(`(?i)(TFTP\s+to\s+Flash\s+Done)`) ) cmd, err := adx.sshClient.PostReCommand(fmt.Sprintf("copy tftp flash %s %s %s", source, filename, location), "#", timeout) if err != nil { return "", err } e := util.RegexpString(respErr, *cmd.Response) if e != "" { return "", errors.New(e) } r := util.RegexpString(respSuc, *cmd.Response) if r != "" { return r, nil } return util.RegexpString(respAny, *cmd.Response), nil } // UpdateBootSource method will set the boot source to the new location specified func (adx *ADX) UpdateBootSource(location string, timeout int) error { var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.CommandCreate("configure terminal", timeout, "#")) cmd = append(cmd, sshAPI.CommandCreate("no boot system flash primary", timeout, "#")) cmd = append(cmd, sshAPI.CommandCreate("no boot system flash secondary", timeout, "#")) cmd = append(cmd, sshAPI.CommandCreate(fmt.Sprintf("boot system flash %v", location), timeout, "#")) cmd = append(cmd, sshAPI.CommandCreate("end", timeout, "#")) cmd = append(cmd, sshAPI.CommandCreate("write memory", timeout, "#")) _, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } return nil } // Reboot method will issue a write mem, and reload the ADX unit. func (adx *ADX) Reboot() error { var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.CommandCreate("write memory", 15, "#")) cmd = append(cmd, sshAPI.CommandCreate("reload-yes", 5, "")) _, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } return nil } func (adx *ADX) POSTVeVridTrackPriority(ve *parsers.AdxRunVEInterface) error { if ve.Interface == 0 { return errors.New("ve interface number not defined") } rVe, err := adx.getRunningVeInterface(ve.Interface) if err != nil { return err } var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 20, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(fmt.Sprintf("interface ve %v", ve.Interface), 20, "#(\\s+$|$)")) if ve.IPv4VRID.ID != 0 { if ve.IPv4VRID.ID != rVe.IPv4VRID.ID { return errors.New(fmt.Sprintf("No matching VRID found for VRID %v", ve.IPv4VRID.ID)) } if ve.IPv4VRID.TrackPriority == "" || ve.IPv4VRID.Priority == "" { return errors.New("track and/or backup priority not set in IPv4 VRID payload") } cmd = append(cmd, sshAPI.ReCommandCreate(fmt.Sprintf("ip vrrp-extended vrid %v", ve.IPv4VRID.ID), 20, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(fmt.Sprintf("backup priority %v track-priority %v", ve.IPv4VRID.Priority, ve.IPv4VRID.TrackPriority), 20, "#(\\s+$|$)")) } if ve.IPv6VRID.ID != 0 { if ve.IPv6VRID.ID != rVe.IPv6VRID.ID { return errors.New("no matching VRID found") } if ve.IPv6VRID.TrackPriority == "" || ve.IPv6VRID.Priority == "" { return errors.New("track and/or backup priority not set in IPv6 VRID payload") } cmd = append(cmd, sshAPI.ReCommandCreate(fmt.Sprintf("ipv6 vrrp-extended vrid %v", ve.IPv6VRID.ID), 20, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(fmt.Sprintf("backup priority %v track-priority %v", ve.IPv6VRID.Priority, ve.IPv6VRID.TrackPriority), 20, "#(\\s+$|$)")) } cmd = append(cmd, sshAPI.ReCommandCreate("end", 20, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 20, "#")) _, err = adx.sshClient.PostCommands(cmd) if err != nil { return err } return adx.GetInterfaceConfigs(strconv.Itoa(ve.Interface)) } func (adx *ADX) getRunningVeInterface(interfaceId int) (parsers.AdxRunVEInterface, error) { if adx.Config.VEInterfaces == nil { if err := adx.GetInterfaceConfigs(""); err != nil { return parsers.AdxRunVEInterface{}, err } } for _, veInt := range adx.Config.VEInterfaces { if veInt.Interface == interfaceId { return veInt, nil } } return parsers.AdxRunVEInterface{}, fmt.Errorf("no matching VE interface found for interface %v", interfaceId) } func (adx *ADX) DeleteSelectBPMask() error { if adx.Config.SelectBPMask == "" { if err := adx.GetServerBPMask(); err != nil { return err } } if adx.Config.SelectBPMask == "" { return errors.New("server select-bp-mask not configured") } var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.CommandCreate("configure terminal", 10, "#")) cmd = append(cmd, sshAPI.CommandCreate(fmt.Sprintf("no server select-bp-mask %s", adx.Config.SelectBPMask), 10, "#")) cmd = append(cmd, sshAPI.CommandCreate("end", 10, "#")) cmd = append(cmd, sshAPI.CommandCreate("write memory", 10, "#")) _, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } return adx.GetServerBPMask() } func (adx *ADX) PutSelectBPMask(mask string) error { var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.CommandCreate("configure terminal", 10, "#")) cmd = append(cmd, sshAPI.CommandCreate(fmt.Sprintf("server select-bp-mask %v", mask), 10, "#")) cmd = append(cmd, sshAPI.CommandCreate("end", 10, "#")) cmd = append(cmd, sshAPI.CommandCreate("write memory", 10, "#")) r, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } for _, cmdResp := range r.Commands { if resp := regexp.MustCompile(`(?i)(Invalid\s+input.*\n)`).FindStringSubmatch(*cmdResp.Response); resp != nil { return errors.New(resp[1]) } } return adx.GetServerBPMask() } func (adx *ADX) POSTPasswordEnforcement() error { var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 20, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("enable strict-password-enforcement", 20, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 20, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 20, "#")) _, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } return adx.GetPasswordEnforcement() } func (adx *ADX) DELETEPasswordEnforcement() error { var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 20, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("no enable strict-password-enforcement", 20, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 20, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 20, "#")) _, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } return adx.GetPasswordEnforcement() } func (adx *ADX) EnablePasswordDisplay(enable bool) error { cmdString := "enable password-display" if !enable { cmdString = "no " + cmdString } var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(cmdString, 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 50, "#")) _, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } return nil } func (adx *ADX) POSTSnmpCommunity(community *parsers.SNMPCommunity) error { //check for needed info (need at least community and ro/rw if community.Name == "" || community.RoRw == "" { return errors.New("incomplete community: Need at least a community and ro/rw") } if !(community.RoRw == "ro" || community.RoRw == "rw") { return errors.New("invalid community data: ro/rw needs to be 'ro' or 'rw'") } cmdString := fmt.Sprintf("snmp-server community %s ", community.Name) if community.PwDisplayNum != "" { cmdString += fmt.Sprintf("%s ", community.PwDisplayNum) } cmdString += community.RoRw var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(cmdString, 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 50, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 50, "#")) _, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } return adx.GetSNMP() } func (adx *ADX) DELETESnmpCommunity(community *parsers.SNMPCommunity) error { //check for needed info (need at least community and ro/rw if community.Name == "" || community.RoRw == "" { return errors.New("incomplete community: Need at least a community and ro/rw") } if !(community.RoRw == "ro" || community.RoRw == "rw") { return errors.New("invalid community data: ro/rw needs to be 'ro' or 'rw'") } cmdString := "no snmp-server community " if community.PwDisplayNum != "" { cmdString += fmt.Sprintf("%s ", community.PwDisplayNum) } cmdString += fmt.Sprintf("%s %s", community.Name, community.RoRw) var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(cmdString, 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 50, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 50, "#")) _, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } return adx.GetSNMP() } func (adx *ADX) POSTSnmpGroup(group *parsers.SNMPGroup, ignoreExisting bool) error { //check for needed info if group.Name == "" || group.Version == "" { return errors.New("incomplete group: Need at least group name and version") } switch group.Version { case "1": group.Version = "v1" case "v2", "2", "2c": group.Version = "v2c" case "3": group.Version = "v3" } cmdString := fmt.Sprintf("snmp-server group %s %s ", group.Name, group.Version) if group.Version == "v3" { switch group.Security { case "auth", "noauth", "priv": cmdString += fmt.Sprintf("%s ", group.Security) default: return errors.New("v3 groups require noauth/auth/priv for group Security") } } if group.Access != "" { cmdString += fmt.Sprintf("access %s ", group.Access) } if group.Read != "" { cmdString += fmt.Sprintf("read %s ", group.Read) } if group.Write != "" { cmdString += fmt.Sprintf("write %s ", group.Write) } if group.Notify != "" { cmdString += fmt.Sprintf("notify %s ", group.Notify) } var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(cmdString, 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 50, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 50, "#")) res, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } prompt := "" for _, l := range res.Commands { response := *l.Response if strings.Contains(l.Command, "configure terminal") { //grab our (config)# line to prevent false errors prompt = response } if l.Command == cmdString { if response != "" { switch { case ignoreExisting && strings.Contains(response, "exist"): //do nothing case len(response) <= len(prompt) && len(response) > 9 && len(prompt) > 9 && response[len(response)-9:] == prompt[len(prompt)-9:]: //do nothing, we just caught our prompt // >9 = (config)# // using the tail end of prompt since the ADX API likes to truncate 1-3 letters from the beginning. default: return errors.New(response) } } } } return adx.GetSNMP() } func (adx *ADX) DELETESnmpGroup(group *parsers.SNMPGroup, ignoreMissing bool) error { //check for needed info if group.Name == "" || group.Version == "" { return errors.New("incomplete group: Need at least group name and version") } switch group.Version { case "1": group.Version = "v1" case "v2", "2", "2c": group.Version = "v2c" case "3": group.Version = "v3" } cmdString := fmt.Sprintf("no snmp-server group %s %s ", group.Name, group.Version) if group.Version == "v3" { switch group.Security { case "auth", "noauth", "priv": cmdString += fmt.Sprintf("%s ", group.Security) default: return errors.New("v3 groups require noauth/auth/priv for group Security") } } var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(cmdString, 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 50, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 50, "#")) res, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } prompt := "" for _, l := range res.Commands { response := *l.Response if strings.Contains(l.Command, "configure terminal") { //grab our (config)# line to prevent false errors prompt = response } if l.Command == cmdString { if response != "" { switch { case ignoreMissing && strings.Contains(response, "exist"): //do nothing case len(response) <= len(prompt) && len(response) > 9 && len(prompt) > 9 && response[len(response)-9:] == prompt[len(prompt)-9:]: //do nothing, we just caught our prompt // >9 = (config)# // using the tail end of prompt since the ADX API likes to truncate 1-3 letters from the beginning. default: return errors.New(response) } } } } return adx.GetSNMP() } func (adx *ADX) POSTSnmpUser(snmpUser *parsers.SNMPUser, ignoreExisting bool) error { //check for needed info if snmpUser.Name == "" || snmpUser.Group == "" { return errors.New("incomplete user: Need at least user name and group") } snmpUser.Version = "v3" cmdString := fmt.Sprintf("snmp-server user %s %s %s ", snmpUser.Name, snmpUser.Group, snmpUser.Version) if snmpUser.Access != "" { cmdString += fmt.Sprintf("access %s ", snmpUser.Access) } if snmpUser.Encrypted { if snmpUser.AuthPass == "" || snmpUser.AuthType == "" { return errors.New("invalid user data: 'encrypted' specified but missing auth type or password") } cmdString += "encrypted " } if snmpUser.AuthType != "" || snmpUser.AuthPass != "" { if (snmpUser.AuthType != "" && snmpUser.AuthPass == "") || (snmpUser.AuthType == "" && snmpUser.AuthPass != "") { return errors.New("invalid user data: Auth type and password must BOTH be populated") } cmdString += fmt.Sprintf("auth %s %s ", snmpUser.AuthType, snmpUser.AuthPass) } if snmpUser.PrivType != "" || snmpUser.PrivPass != "" { if (snmpUser.PrivType != "" && snmpUser.PrivPass == "") || (snmpUser.PrivType == "" && snmpUser.PrivPass != "") { return errors.New("invalid user data: Priv type and password must BOTH be populated") } cmdString += "priv " if snmpUser.Encrypted { cmdString += "encrypted " } cmdString += fmt.Sprintf("%s %s ", snmpUser.PrivType, snmpUser.PrivPass) } var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(cmdString, 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 50, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 50, "#")) res, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } prompt := "" for _, l := range res.Commands { response := *l.Response if strings.Contains(l.Command, "configure terminal") { //grab our (config)# line to prevent false errors prompt = response } if l.Command == cmdString { if response != "" { switch { case ignoreExisting && strings.Contains(response, "exist"): //do nothing case len(response) <= len(prompt) && len(response) > 9 && len(prompt) > 9 && response[len(response)-9:] == prompt[len(prompt)-9:]: //do nothing, we just caught our prompt // >9 = (config)# // using the tail end of prompt since the ADX API likes to truncate 1-3 letters from the beginning. default: return errors.New(response) } } } } return adx.GetSNMP() } func (adx *ADX) DELETESnmpUser(snmpUser *parsers.SNMPUser, ignoreMissing bool) error { //check for needed info if snmpUser.Name == "" || snmpUser.Group == "" { return errors.New("incomplete user: Need at least user name and group") } snmpUser.Version = "v3" cmdString := fmt.Sprintf("no snmp-server user %s %s %s ", snmpUser.Name, snmpUser.Group, snmpUser.Version) var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(cmdString, 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 50, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 50, "#")) res, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } prompt := "" for _, l := range res.Commands { response := *l.Response if strings.Contains(l.Command, "configure terminal") { //grab our (config)# line to prevent false errors prompt = response } if l.Command == cmdString { if response != "" { switch { case ignoreMissing && strings.Contains(response, "exist"): //do nothing case len(response) <= len(prompt) && len(response) > 9 && len(prompt) > 9 && response[len(response)-9:] == prompt[len(prompt)-9:]: //do nothing, we just caught our prompt // >9 = (config)# // using the tail end of prompt since the ADX API likes to truncate 1-3 letters from the beginning. default: return errors.New(response) } } } } return adx.GetSNMP() } func (adx *ADX) POSTSnmpHost(host *parsers.SNMPHost, ignoreExisting bool) error { //check for needed info if host.Address == "" || host.Version == "" { return errors.New("incomplete host: Need at least address and version") } switch host.Version { case "1": host.Version = "v1" case "v2", "2", "2c": host.Version = "v2c" case "3": host.Version = "v3" } cmdString := fmt.Sprintf("snmp-server host %s version %s ", host.Address, host.Version) switch host.Version { case "v1", "v2c": if host.Community == "" { return errors.New("invalid host data: version 1 or 2c hosts must include community") } cmdString += fmt.Sprintf("%s ", host.Community) case "v3": if host.Security == "" || host.User == "" { return errors.New("invalid host data: version 3 hosts must include security level(noauth/auth/priv) and user") } if !(host.Security == "noauth" || host.Security == "auth" || host.Security == "priv") { return errors.New("invalid host security level: version 3 hosts must include security level(noauth/auth/priv)") } cmdString += fmt.Sprintf("%s %s ", host.Security, host.User) default: return errors.New("invalid host data: version must be v1, v2c, or v3") } if host.Port != "" { if _, numErr := strconv.Atoi(host.Port); numErr != nil { return errors.New("invalid host port: Must be numerical") } cmdString += fmt.Sprintf("port %s ", host.Port) } if !ignoreExisting { //check for existing (adx will not show error, it will just overwrite existing) for _, h := range adx.Config.SNMP.Hosts { if h.Address == host.Address && h.Version == host.Version { return fmt.Errorf("host '%s' (version %s) already exists", host.Address, host.Version) } } } var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(cmdString, 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 50, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 50, "#")) res, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } prompt := "" for _, l := range res.Commands { response := *l.Response if strings.Contains(l.Command, "configure terminal") { //grab our (config)# line to prevent false errors prompt = response } if l.Command == cmdString { if response != "" { switch { case len(response) <= len(prompt) && len(response) > 9 && len(prompt) > 9 && response[len(response)-9:] == prompt[len(prompt)-9:]: //do nothing, we just caught our prompt // >9 = (config)# // using the tail end of prompt since the ADX API likes to truncate 1-3 letters from the beginning. default: return errors.New(response) } } } } return adx.GetSNMP() } func (adx *ADX) DELETESnmpHost(host *parsers.SNMPHost, ignoreMissing bool) error { //check for needed info if host.Address == "" || host.Version == "" { return errors.New("incomplete host: Need at least address and version") } switch host.Version { case "1": host.Version = "v1" case "v2", "2", "2c": host.Version = "v2c" case "3": host.Version = "v3" } if !(host.Version == "v1" || host.Version == "v2c" || host.Version == "v3") { return errors.New("invalid host data: version must be v1, v2c, or v3") } cmdString := fmt.Sprintf("no snmp-server host %s version %s ", host.Address, host.Version) if host.Version == "v3" { if host.Security == "" || host.User == "" { return errors.New("invalid host data: version 3 hosts must include security level(noauth/auth/priv) and user") } if !(host.Security == "noauth" || host.Security == "auth" || host.Security == "priv") { return errors.New("invalid host security level: version 3 hosts must include security level(noauth/auth/priv)") } cmdString += fmt.Sprintf("%s %s ", host.Security, host.User) } var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(cmdString, 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 50, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 50, "#")) res, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } prompt := "" for _, l := range res.Commands { response := *l.Response if strings.Contains(l.Command, "configure terminal") { //grab our (config)# line to prevent false errors prompt = response } if l.Command == cmdString { if response != "" { switch { case ignoreMissing && (strings.Contains(response, "exist") || strings.Contains(response, "cannot configure trap host")): //do nothing case len(response) <= len(prompt) && len(response) > 9 && len(prompt) > 9 && response[len(response)-9:] == prompt[len(prompt)-9:]: //do nothing, we just caught our prompt // >9 = (config)# // using the tail end of prompt since the ADX API likes to truncate 1-3 letters from the beginning. default: return errors.New(response) } } } } return adx.GetSNMP() } func (adx *ADX) POSTRoute(network, netmask, nextHop string) error { if network == "" || netmask == "" || nextHop == "" { return errors.New("network, netmask, and nextHop must all be populated") } cmdString := fmt.Sprintf("ip route %s %s %s", network, netmask, nextHop) var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(cmdString, 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 50, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 50, "#")) res, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } prompt := "" for _, l := range res.Commands { response := *l.Response if strings.Contains(l.Command, "configure terminal") { //grab our (config)# line to prevent false errors prompt = response } if l.Command == cmdString { if response != "" { switch { case len(response) <= len(prompt) && len(response) > 9 && len(prompt) > 9 && response[len(response)-9:] == prompt[len(prompt)-9:]: //do nothing, we just caught our prompt // >9 = (config)# // using the tail end of prompt since the ADX API likes to truncate 1-3 letters from the beginning. default: return errors.New(response) } } } } return adx.GetRouteConfig() } func (adx *ADX) DELETERoute(network, netmask, nextHop string) error { if network == "" || netmask == "" || nextHop == "" { return errors.New("network, netmask, and nextHop must all be populated") } cmdString := fmt.Sprintf("no ip route %s %s %s", network, netmask, nextHop) var cmd []*sshAPI.Command cmd = append(cmd, sshAPI.ReCommandCreate("configure terminal", 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate(cmdString, 50, "#(\\s+$|$)")) cmd = append(cmd, sshAPI.ReCommandCreate("end", 50, "#")) cmd = append(cmd, sshAPI.ReCommandCreate("write memory", 50, "#")) res, err := adx.sshClient.PostCommands(cmd) if err != nil { return err } prompt := "" for _, l := range res.Commands { response := *l.Response if strings.Contains(l.Command, "configure terminal") { //grab our (config)# line to prevent false errors prompt = response } if l.Command == cmdString { if response != "" { switch { case len(response) <= len(prompt) && len(response) > 9 && len(prompt) > 9 && response[len(response)-9:] == prompt[len(prompt)-9:]: //do nothing, we just caught our prompt // >9 = (config)# // using the tail end of prompt since the ADX API likes to truncate 1-3 letters from the beginning. default: return errors.New(response) } } } } return adx.GetRouteConfig() }