climc_ssh_command.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package command
  15. import (
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "os/exec"
  20. "strings"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/onecloud/pkg/apis/webconsole"
  23. "yunion.io/x/onecloud/pkg/mcclient"
  24. "yunion.io/x/onecloud/pkg/webconsole/helper"
  25. )
  26. type ClimcSshCommand struct {
  27. *BaseCommand
  28. Info *webconsole.ClimcSshInfo
  29. s *mcclient.ClientSession
  30. keyFile string
  31. buffer []byte
  32. }
  33. func NewClimcSshCommand(info *webconsole.ClimcSshInfo, s *mcclient.ClientSession) (*ClimcSshCommand, error) {
  34. if info.IpAddr == "" {
  35. return nil, fmt.Errorf("Empty host ip address")
  36. }
  37. if info.Username == "" {
  38. return nil, fmt.Errorf("Empty username")
  39. }
  40. privateKey, err := helper.GetValidPrivateKey(info.IpAddr, 22, info.Username, "")
  41. if err != nil {
  42. return nil, errors.Wrap(err, "get cloud admin private key")
  43. }
  44. file, err := ioutil.TempFile("", fmt.Sprintf("id_rsa.%s.", info.IpAddr))
  45. if err != nil {
  46. return nil, err
  47. }
  48. defer file.Close()
  49. filename := file.Name()
  50. {
  51. err = os.Chmod(filename, 0600)
  52. if err != nil {
  53. return nil, err
  54. }
  55. _, err = file.Write([]byte(privateKey))
  56. if err != nil {
  57. return nil, err
  58. }
  59. }
  60. env := map[string]string{
  61. "OS_AUTH_TOKEN": s.GetToken().GetTokenString(),
  62. "OS_PROJECT_NAME": s.GetProjectName(),
  63. "OS_PROJECT_DOMAIN": s.GetProjectDomain(),
  64. "YUNION_USE_CACHED_TOKEN": "false",
  65. "OS_TRY_TERM_WIDTH": "false",
  66. }
  67. if len(info.Env) != 0 {
  68. env = info.Env
  69. }
  70. envCmd := ""
  71. for k, v := range env {
  72. envCmd = fmt.Sprintf("%s export %s=%s", envCmd, k, v)
  73. }
  74. execCmd := "exec bash"
  75. if info.Command != "" {
  76. execCmd = info.Command
  77. execCmd = fmt.Sprintf("%s %s", execCmd, strings.Join(info.Args, " "))
  78. }
  79. sshArgs := []string{
  80. "-t", // force pseudo-terminal allocation
  81. "-o", "StrictHostKeyChecking=no",
  82. "-i", filename,
  83. fmt.Sprintf("%s@%s", info.Username, info.IpAddr),
  84. fmt.Sprintf("'%s && %s'", envCmd, execCmd),
  85. }
  86. sshCmd := fmt.Sprintf("ssh %s", strings.Join(sshArgs, " "))
  87. args := []string{"-c", sshCmd}
  88. bCmd := NewBaseCommand(s, "bash", args...)
  89. cmd := &ClimcSshCommand{
  90. BaseCommand: bCmd,
  91. Info: info,
  92. s: s,
  93. keyFile: filename,
  94. buffer: []byte{},
  95. }
  96. return cmd, nil
  97. }
  98. func (c ClimcSshCommand) GetCommand() *exec.Cmd {
  99. cmd := c.BaseCommand.GetCommand()
  100. cmd.Env = append(cmd.Env, "TERM=xterm-256color")
  101. return cmd
  102. }
  103. func (c ClimcSshCommand) GetInstanceName() string {
  104. if c.Info.DisplayInfo == nil {
  105. return ""
  106. }
  107. return c.Info.DisplayInfo.InstanceName
  108. }
  109. func (c ClimcSshCommand) GetIPs() []string {
  110. if c.Info.DisplayInfo == nil {
  111. return nil
  112. }
  113. return c.Info.DisplayInfo.IPs
  114. }
  115. func (c ClimcSshCommand) GetProtocol() string {
  116. return PROTOCOL_TTY
  117. }
  118. func (c ClimcSshCommand) Cleanup() error {
  119. if len(c.keyFile) > 0 {
  120. os.Remove(c.keyFile)
  121. c.keyFile = ""
  122. }
  123. return nil
  124. }
  125. func (c *ClimcSshCommand) Scan(d byte, send func(msg string)) {
  126. switch d {
  127. case '\r': // 换行
  128. send("\r\n")
  129. c.buffer = []byte{}
  130. case '\u007f': // 退格
  131. if len(c.buffer) > 0 {
  132. c.buffer = c.buffer[:len(c.buffer)-1]
  133. send("\b \b")
  134. }
  135. default:
  136. c.buffer = append(c.buffer, d)
  137. }
  138. }