resolve_sshinfo.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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 session
  15. import (
  16. "context"
  17. "yunion.io/x/jsonutils"
  18. "yunion.io/x/pkg/errors"
  19. compute_api "yunion.io/x/onecloud/pkg/apis/compute"
  20. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  21. "yunion.io/x/onecloud/pkg/httperrors"
  22. "yunion.io/x/onecloud/pkg/mcclient"
  23. "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  24. modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  25. options "yunion.io/x/onecloud/pkg/mcclient/options/compute"
  26. )
  27. type SSshConnectionInfo struct {
  28. IP string `json:"ip"`
  29. Port int `json:"port"`
  30. Username string `json:"username"`
  31. KeepUsername bool `json:"keep_username"`
  32. Password string `json:"password"`
  33. Name string `json:"name"`
  34. ResourceType string `json:"resource_type" choices:"host|server"`
  35. GuestDetails *compute_api.ServerDetails
  36. HostDetails *compute_api.HostDetails
  37. }
  38. func ResolveServerSSHIPPortById(ctx context.Context, s *mcclient.ClientSession, id string, ip string, port int) (string, int, *compute_api.ServerDetails, error) {
  39. if port <= 0 {
  40. port = 22
  41. }
  42. return resolveServerIPPortById(ctx, s, id, ip, port)
  43. }
  44. func resolveServerIPPortById(ctx context.Context, s *mcclient.ClientSession, id string, ip string, port int) (string, int, *compute_api.ServerDetails, error) {
  45. guestDetails, err := FetchServerInfo(ctx, s, id)
  46. if err != nil {
  47. return "", 0, nil, errors.Wrap(err, "fetchServerInfo")
  48. }
  49. // list all nic of a server
  50. input := compute_api.GuestnetworkListInput{}
  51. input.ServerId = guestDetails.Id
  52. True := true
  53. input.Details = &True
  54. input.ServerFilterListInput.Scope = "max"
  55. result, err := compute.Servernetworks.List(s, jsonutils.Marshal(input))
  56. if err != nil {
  57. return "", 0, nil, errors.Wrap(err, "Servernetworks.List")
  58. }
  59. if result.Total == 0 {
  60. // not nic found!!!
  61. return "", 0, nil, errors.Wrap(httperrors.ErrNotFound, "no nic on server")
  62. }
  63. // find nics
  64. var guestNicDetails *compute_api.GuestnetworkDetails
  65. if result.Total == 1 {
  66. gn := compute_api.GuestnetworkDetails{}
  67. err := result.Data[0].Unmarshal(&gn)
  68. if err != nil {
  69. return "", 0, nil, errors.Wrap(err, "Unmarshal guest network info")
  70. }
  71. if len(ip) > 0 && ip != gn.EipAddr && ip != gn.IpAddr && ip != gn.Ip6Addr && ip != guestDetails.Eip {
  72. return "", 0, nil, errors.Wrapf(httperrors.ErrInputParameter, "ip %s not match with server", ip)
  73. }
  74. guestNicDetails = &gn
  75. } else {
  76. if len(ip) == 0 {
  77. return "", 0, nil, errors.Wrap(httperrors.ErrInputParameter, "must specify ip")
  78. }
  79. for _, gnJson := range result.Data {
  80. gn := compute_api.GuestnetworkDetails{}
  81. err := gnJson.Unmarshal(&gn)
  82. if err != nil {
  83. return "", 0, nil, errors.Wrap(err, "Unmarshal guest network info")
  84. }
  85. if ip == gn.EipAddr || ip == gn.IpAddr || ip == gn.Ip6Addr {
  86. guestNicDetails = &gn
  87. break
  88. }
  89. }
  90. if guestNicDetails == nil {
  91. return "", 0, nil, errors.Wrap(httperrors.ErrInputParameter, "ip specified not match with server")
  92. }
  93. }
  94. if len(ip) == 0 {
  95. // guest ip
  96. if len(guestNicDetails.EipAddr) > 0 {
  97. ip = guestNicDetails.EipAddr
  98. } else if len(guestNicDetails.IpAddr) > 0 {
  99. ip = guestNicDetails.IpAddr
  100. } else if len(guestNicDetails.Ip6Addr) > 0 {
  101. ip = guestNicDetails.Ip6Addr
  102. } else {
  103. return "", 0, nil, errors.Wrap(httperrors.ErrNotSupported, "no valid ipv4 addr")
  104. }
  105. }
  106. if ip == guestNicDetails.IpAddr && len(guestNicDetails.MappedIpAddr) > 0 {
  107. // need to do open forward
  108. ip, port, err = acquireForward(ctx, s, guestDetails.Id, ip, "tcp", port)
  109. if err != nil {
  110. return "", 0, nil, errors.Wrap(err, "acquireForward")
  111. }
  112. }
  113. return ip, port, guestDetails, nil
  114. }
  115. func ResolveHostSSHIPPortById(ctx context.Context, s *mcclient.ClientSession, id string, ip string, port int) (string, int, *compute_api.HostDetails, error) {
  116. if port <= 0 {
  117. port = 22
  118. }
  119. hostDetails, err := FetchHostInfo(ctx, s, id)
  120. if err != nil {
  121. return "", 0, nil, errors.Wrap(err, "fetchServerInfo")
  122. }
  123. return hostDetails.AccessIp, port, hostDetails, nil
  124. }
  125. type sForwardInfo struct {
  126. ProxyAddr string `json:"proxy_addr"`
  127. ProxyPort int `json:"proxy_port"`
  128. }
  129. func acquireForward(ctx context.Context, session *mcclient.ClientSession, srvid string, ip string, proto string, port int) (string, int, error) {
  130. lockman.LockRawObject(ctx, "server", srvid)
  131. defer lockman.ReleaseRawObject(ctx, "server", srvid)
  132. addr, nport, err := listForward(session, srvid, ip, proto, port)
  133. if err == nil {
  134. return addr, nport, nil
  135. }
  136. if errors.Cause(err) == httperrors.ErrNotFound {
  137. return openForward(session, srvid, ip, proto, port)
  138. } else {
  139. return "", 0, errors.Wrap(err, "listForward")
  140. }
  141. }
  142. func listForward(session *mcclient.ClientSession, srvid string, ip string, proto string, port int) (string, int, error) {
  143. opt := &options.ServerListForwardOptions{
  144. ServerIdOptions: options.ServerIdOptions{
  145. ID: srvid,
  146. },
  147. Proto: &proto,
  148. Port: &port,
  149. Addr: &ip,
  150. }
  151. params, err := opt.Params()
  152. if err != nil {
  153. return "", 0, errors.Wrap(err, "get list forward params")
  154. }
  155. jsonItem, err := modules.Servers.PerformAction(session, opt.ID, "list-forward", params)
  156. if err != nil {
  157. return "", 0, errors.Wrap(err, "list-forward")
  158. }
  159. if jsonItem.Contains("forwards") {
  160. infoList := make([]sForwardInfo, 0)
  161. err = jsonItem.Unmarshal(&infoList, "forwards")
  162. if err != nil {
  163. return "", 0, errors.Wrap(err, "Unmarshal forwards")
  164. }
  165. if len(infoList) > 0 {
  166. return infoList[0].ProxyAddr, infoList[0].ProxyPort, nil
  167. }
  168. }
  169. return "", 0, errors.Wrap(httperrors.ErrNotFound, "no forwards")
  170. }
  171. func openForward(session *mcclient.ClientSession, srvid string, ip string, proto string, port int) (string, int, error) {
  172. opt := &options.ServerOpenForwardOptions{
  173. ServerIdOptions: options.ServerIdOptions{
  174. ID: srvid,
  175. },
  176. Proto: proto,
  177. Port: port,
  178. Addr: ip,
  179. }
  180. params, err := opt.Params()
  181. if err != nil {
  182. return "", 0, errors.Wrap(err, "get open forward params")
  183. }
  184. jsonItem, err := modules.Servers.PerformAction(session, opt.ID, "open-forward", params)
  185. if err != nil {
  186. return "", 0, errors.Wrap(err, "open-forward")
  187. }
  188. info := sForwardInfo{}
  189. err = jsonItem.Unmarshal(&info)
  190. if err != nil {
  191. return "", 0, errors.Wrap(err, "Unmarshal")
  192. }
  193. return info.ProxyAddr, info.ProxyPort, nil
  194. }