package sshAPI import ( "bytes" "encoding/json" "errors" "fmt" "net/http" "github.rackspace.com/gorax/clients/coreAPI" "github.rackspace.com/gorax/webutil" ) type ( SSHCommandRequest struct { Host string `json:"host"` TimeOut int `json:"timeout"` Platform string `json:"platform"` Command []*SSHCommand `json:"commands"` UserData UserData `json:"user_info"` Context *string `json:"context,omitempty"` } SSHCommands struct { Host string `json:"host"` Commands []*Command `json:"commands"` Platform string `json:"platform"` Context *string `json:"context,omitempty"` UserData UserData `json:"user_info"` deviceId string } Command struct { Command string `json:"command"` Prompt string `json:"prompt"` RePrompt *string `json:"re_prompt"` Timeout int `json:"timeout"` Response *string `json:"response"` } SSHCommand struct { Host string `json:"host"` Command string `json:"command"` Prompt string `json:"prompt"` RePrompt *string `json:"re_prompt"` Timeout int `json:"timeout"` Response *string `json:"response"` Platform string `json:"platform"` Context *string `json:"context,omitempty"` UserData UserData `json:"user_info"` deviceId string } UserData struct { Username string `json:"username"` UUID string EmployeeId int `json:"employee_id"` } SSHCommandsResponse struct { Data *SSHCommands `json:"data"` } SSHCommandResponse struct { Data *SSHCommand `json:"data"` } ) func (s *SSHCommand) String() string { if s.Response == nil { return "" } return *s.Response } func (s *SSHCommand) SSHCommand() *Command { return &Command{ Command: s.Command, Prompt: s.Prompt, Response: s.Response, } } func (s *SwitchClient) PostCommand(cmd, prompt string, timeout int) (*SSHCommand, error) { sc := &SSHCommand{ Host: s.IPAddress, Command: cmd, Timeout: timeout, Prompt: prompt, Platform: "switch", deviceId: s.Host, } return sc.Post(false, "") } func (s *SwitchClient) PostReCommand(cmd, prompt string, timeout int) (*SSHCommand, error) { sc := &SSHCommand{ Host: s.IPAddress, Command: cmd, Timeout: timeout, RePrompt: &prompt, Platform: "switch", deviceId: s.Host, } return sc.Post(false, "") } func (s *SwitchClient) PostCommands(commands []*Command) (*SSHCommands, error) { sc := &SSHCommands{ Host: s.IPAddress, Commands: commands, Platform: "switch", deviceId: s.Host, } return sc.Post(false, "") } // PostReCommand : Send multiple commands to the device, automatically append \n to each func (s *SSHClient) PostCommands(commands []*Command) (*SSHCommands, error) { h := s.connectIP() if h == nil { return nil, errNoIP } sc := &SSHCommands{ Host: *h, Commands: commands, deviceId: s.device.ID, Platform: s.platform, Context: s.Context, UserData: UserData{ Username: s.core.SessionInfo.Username, UUID: s.xConnectID, }, } return sc.Post(s.device.IsPix(), s.deviceType) } // PostReCommand : Send a command to the device, automatically appending \n func (s *SSHClient) PostCommand(cmd, prompt string, timeout int) (*SSHCommand, error) { h := s.connectIP() if h == nil { return nil, errNoIP } sc := &SSHCommand{ Host: *h, Command: cmd, Timeout: timeout, Prompt: prompt, deviceId: s.device.ID, Platform: s.platform, Context: s.Context, UserData: UserData{ Username: s.core.SessionInfo.Username, UUID: s.xConnectID, }, } return sc.Post(s.device.IsPix(), s.deviceType) } // PostReCommand : Send multiple commands to the device, automatically append \n to each, prompt is in regex format func (s *SSHClient) PostReCommand(cmd, prompt string, timeout int) (*SSHCommand, error) { h := s.connectIP() if h == nil { return nil, errNoIP } sc := &SSHCommand{ Host: *h, Command: cmd, Timeout: timeout, RePrompt: &prompt, deviceId: s.device.ID, Platform: s.platform, Context: s.Context, UserData: UserData{ Username: s.core.SessionInfo.Username, UUID: s.xConnectID, }, } return sc.Post(s.device.IsPix(), s.deviceType) } // SendText : Send text to the device without appending a \n to it func (s *SSHClient) SendText(cmd, prompt string, timeout int) (*SSHCommand, error) { h := s.connectIP() if h == nil { return nil, errNoIP } sc := &SSHCommand{ Host: *h, Command: cmd, Timeout: timeout, Prompt: prompt, deviceId: s.device.ID, Platform: s.platform, Context: s.Context, UserData: UserData{ Username: s.core.SessionInfo.Username, UUID: s.xConnectID, }, } if s.device.IsPix() { return nil, errors.New("pix not supported for sendtext command") } return sc.Send(s.deviceType) } // SendReText : Send text to the device without appending a \n to it. Prompt is regex string func (s *SSHClient) SendReText(cmd, prompt string, timeout int) (*SSHCommand, error) { h := s.connectIP() if h == nil { return nil, errNoIP } sc := &SSHCommand{ Host: *h, Command: cmd, Timeout: timeout, RePrompt: &prompt, deviceId: s.device.ID, Platform: s.platform, Context: s.Context, UserData: UserData{ Username: s.core.SessionInfo.Username, UUID: s.xConnectID, }, } if s.device.IsPix() { return nil, errors.New("pix not supported for sendtext command") } return sc.Send(s.deviceType) } func (s *SSHCommands) Post(pix bool, deviceType string) (*SSHCommands, error) { js, err := json.Marshal(s) if err != nil { return nil, err } path := NDAPI + "direct/cmds" if pix { path = NDAPI + "pix/cmds" } req, err := http.NewRequest("POST", path, bytes.NewBuffer(js)) if err != nil { return nil, fmt.Errorf("error creating new http request: %s", err.Error()) } req.Header.Set("device", s.deviceId) req.Header.Set("Content-Type", "application/json") qry := req.URL.Query() qry.Add("auth", deviceType) req.URL.RawQuery = qry.Encode() if s.UserData.UUID != "" { req.Header.Set("x-request-id", s.UserData.UUID) } client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, err } defer func() { _ = resp.Body.Close() }() res, code, err := webutil.ResponseCheck(resp) if err != nil { return nil, err } if code != 200 { return nil, errors.New(string(res)) } var cr SSHCommandsResponse if err = json.Unmarshal(res, &cr); err != nil { return nil, err } return cr.Data, nil } func (s *SSHCommand) Post(pix bool, deviceType string) (*SSHCommand, error) { js, err := json.Marshal(s) if err != nil { return nil, err } path := NDAPI + "direct/cmd" if pix { path = NDAPI + "pix/cmd" } req, err := http.NewRequest("POST", path, bytes.NewBuffer(js)) if err != nil { return nil, fmt.Errorf("error creating new http request: %s", err.Error()) } req.Header.Set("device", s.deviceId) req.Header.Set("Content-Type", "application/json") qry := req.URL.Query() qry.Add("auth", deviceType) req.URL.RawQuery = qry.Encode() if s.UserData.UUID != "" { req.Header.Set("x-request-id", s.UserData.UUID) } client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, err } defer func() { _ = resp.Body.Close() }() res, code, err := webutil.ResponseCheck(resp) if err != nil { return nil, err } if code != 200 { return nil, errors.New(string(res)) } var cr SSHCommandResponse if err = json.Unmarshal(res, &cr); err != nil { return nil, err } return cr.Data, nil } // Send : Utilizes the sshapi 'send text' method for transmitting text without a newline automatically appended func (s *SSHCommand) Send(deviceType string) (*SSHCommand, error) { js, err := json.Marshal(s) if err != nil { return nil, fmt.Errorf("error marshalling SSHCommand to JSON: %w", err) } path := NDAPI + "sendtext" req, err := http.NewRequest("POST", path, bytes.NewBuffer(js)) if err != nil { return nil, fmt.Errorf("error creating new http request: %w", err) } req.Header.Set("device", s.deviceId) req.Header.Set("Content-Type", "application/json") qry := req.URL.Query() qry.Add("auth", deviceType) req.URL.RawQuery = qry.Encode() if s.UserData.UUID != "" { req.Header.Set("x-request-id", s.UserData.UUID) } client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, err } defer func() { _ = resp.Body.Close() }() res, code, err := webutil.ResponseCheck(resp) if err != nil { return nil, err } if code != 200 { // attempt to parse the error in to a structure if e := UnmarshalAPIError(res); e != nil { return nil, e } // otherwise return raw response return nil, errors.New(string(res)) } var cr SSHCommandResponse if err = json.Unmarshal(res, &cr); err != nil { return nil, fmt.Errorf("error unmarshalling response: %w", err) } return cr.Data, nil } func (s *SSHClient) connectIP() *string { if s.UsePublic { if s.device.PrimaryIP != "" { return &s.device.PrimaryIP } return nil } if s.device.DracIP != "" { return &s.device.DracIP } if s.device.PrimaryIP != "" { return &s.device.PrimaryIP } return nil } func (s *SSHCommands) PostCommands(platform, bastion, proxy string, device *coreAPI.DeviceInfo, direct bool) (*SSHCommands, error) { host := device.DracIP if host == "" { host = device.PrimaryIP } if host == "" { return nil, errNoIP } if direct { if device.PrimaryIP == "" { return nil, errNoIP } host = device.PrimaryIP } if proxy == "" { proxy = device.DataCenter } s.Host = host js, err := json.Marshal(s) if err != nil { return nil, err } path := NDAPI + "direct/cmds" if device.IsPix() { path = NDAPI + "pix/cmds" } req, err := http.NewRequest("POST", path, bytes.NewBuffer(js)) if err != nil { return nil, fmt.Errorf("error creating new http request: %s", err.Error()) } req.Header.Set("device", s.deviceId) req.Header.Set("Content-Type", "application/json") if s.UserData.UUID != "" { req.Header.Set("x-request-id", s.UserData.UUID) } client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, err } defer func() { _ = resp.Body.Close() }() res, code, err := webutil.ResponseCheck(resp) if err != nil { return nil, err } if code != 200 { return nil, errors.New(string(res)) } var cr SSHCommandsResponse if err = json.Unmarshal(res, &cr); err != nil { return nil, err } return cr.Data, nil } func CommandCreate(command string, timeout int, prompt string) *Command { return &Command{ Command: command, Timeout: timeout, Prompt: prompt, } } func ReCommandCreate(command string, timeout int, prompt string) *Command { return &Command{ Command: command, Timeout: timeout, RePrompt: &prompt, } } func CommandList(commands []string, timeout int, prompt string) (commandList []*Command) { for _, cmd := range commands { commandList = append(commandList, &Command{ Command: cmd, Timeout: timeout, Prompt: prompt, }) } return }