proxy_endpoints.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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 models
  15. import (
  16. "context"
  17. "strconv"
  18. "yunion.io/x/jsonutils"
  19. "yunion.io/x/log"
  20. "yunion.io/x/pkg/errors"
  21. "yunion.io/x/sqlchemy"
  22. cloudproxy_api "yunion.io/x/onecloud/pkg/apis/cloudproxy"
  23. compute_apis "yunion.io/x/onecloud/pkg/apis/compute"
  24. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  25. "yunion.io/x/onecloud/pkg/cloudcommon/validators"
  26. "yunion.io/x/onecloud/pkg/httperrors"
  27. "yunion.io/x/onecloud/pkg/mcclient"
  28. )
  29. // Add revision?
  30. type SProxyEndpoint struct {
  31. db.SVirtualResourceBase
  32. User string `nullable:"false" list:"user" update:"user" create:"optional"`
  33. Host string `nullable:"false" list:"user" update:"user" create:"required"`
  34. Port int `nullable:"false" list:"user" update:"user" create:"optional"`
  35. PrivateKey string `nullable:"false" update:"user" list:"admin" get:"admin" create:"required"` // do not allow get, list
  36. IntranetIpAddr string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"required"`
  37. StatusDetail string `width:"128" charset:"ascii" nullable:"false" default:"init" list:"user" create:"optional" json:"status_detail"`
  38. }
  39. type SProxyEndpointManager struct {
  40. db.SVirtualResourceBaseManager
  41. }
  42. var ProxyEndpointManager *SProxyEndpointManager
  43. func init() {
  44. ProxyEndpointManager = &SProxyEndpointManager{
  45. SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
  46. SProxyEndpoint{},
  47. "proxy_endpoints_tbl",
  48. "proxy_endpoint",
  49. "proxy_endpoints",
  50. ),
  51. }
  52. ProxyEndpointManager.SetVirtualObject(ProxyEndpointManager)
  53. }
  54. func (man *SProxyEndpointManager) PerformCreateFromServer(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *cloudproxy_api.ProxyEndpointCreateFromServerInput) (jsonutils.JSONObject, error) {
  55. serverId := input.ServerId
  56. if serverId == "" {
  57. return nil, httperrors.NewBadRequestError("server_id is required")
  58. }
  59. serverInfo, err := getServerInfo(ctx, userCred, serverId)
  60. if err != nil {
  61. return nil, err
  62. }
  63. if serverInfo.PrivateKey == "" {
  64. return nil, httperrors.NewBadRequestError("cannot find ssh private key for this server")
  65. }
  66. nic := serverInfo.GetNic()
  67. if nic == nil {
  68. return nil, httperrors.NewBadRequestError("cannot find usable network interface for this server")
  69. }
  70. host := serverInfo.Server.Eip
  71. if host == "" && nic.VpcId == compute_apis.DEFAULT_VPC_ID {
  72. host = nic.IpAddr
  73. }
  74. if host == "" {
  75. return nil, httperrors.NewBadRequestError("cannot find ssh host ip address for this server")
  76. }
  77. var name string
  78. if len(input.Name) > 0 {
  79. name = input.Name
  80. } else if len(input.GenerateName) > 0 {
  81. name, err = db.GenerateName(ctx, man, userCred, input.GenerateName)
  82. if err != nil {
  83. return nil, errors.Wrapf(err, "db.GenerateName")
  84. }
  85. } else {
  86. name, err = db.GenerateName(ctx, man, userCred, serverInfo.Server.Name)
  87. if err != nil {
  88. return nil, errors.Wrapf(err, "db.GenerateName")
  89. }
  90. }
  91. if err := db.NewNameValidator(ctx, man, userCred, name, nil); err != nil {
  92. return nil, httperrors.NewGeneralError(err)
  93. }
  94. port := 22
  95. if portStr, ok := serverInfo.Server.Metadata[compute_apis.SSH_PORT]; ok {
  96. port, _ = strconv.Atoi(portStr)
  97. }
  98. proxyendpoint := &SProxyEndpoint{
  99. User: "cloudroot",
  100. Host: host,
  101. Port: port,
  102. PrivateKey: serverInfo.PrivateKey,
  103. IntranetIpAddr: nic.IpAddr,
  104. }
  105. proxyendpoint.SetModelManager(man, proxyendpoint)
  106. proxyendpoint.Name = name
  107. proxyendpoint.DomainId = serverInfo.Server.DomainId
  108. proxyendpoint.ProjectId = serverInfo.Server.ProjectId
  109. if err := proxyendpoint.remoteCheckMake(ctx, userCred); err != nil {
  110. return nil, err
  111. }
  112. if err := man.TableSpec().Insert(ctx, proxyendpoint); err != nil {
  113. return nil, httperrors.NewServerError("database insertion error: %v", err)
  114. }
  115. var proxymatches []*SProxyMatch
  116. if nic.VpcId != "" {
  117. pm := &SProxyMatch{
  118. ProxyEndpointId: proxyendpoint.Id,
  119. MatchScope: cloudproxy_api.PM_SCOPE_VPC,
  120. MatchValue: nic.VpcId,
  121. }
  122. pm.Name = "vpc-" + nic.VpcId
  123. proxymatches = append(proxymatches, pm)
  124. }
  125. if nic.NetworkId != "" {
  126. pm := &SProxyMatch{
  127. ProxyEndpointId: proxyendpoint.Id,
  128. MatchScope: cloudproxy_api.PM_SCOPE_NETWORK,
  129. MatchValue: nic.NetworkId,
  130. }
  131. pm.Name = "network-" + nic.NetworkId
  132. proxymatches = append(proxymatches, pm)
  133. }
  134. for _, proxymatch := range proxymatches {
  135. proxymatch.DomainId = userCred.GetProjectDomainId()
  136. proxymatch.ProjectId = userCred.GetProjectId()
  137. if err := ProxyMatchManager.TableSpec().Insert(ctx, proxymatch); err != nil {
  138. log.Errorf("failed insertion of proxy match %s: %v", proxymatch.Name, err)
  139. }
  140. }
  141. return jsonutils.Marshal(proxyendpoint), nil
  142. }
  143. func (man *SProxyEndpointManager) ValidateCreateData(
  144. ctx context.Context,
  145. userCred mcclient.TokenCredential,
  146. ownerId mcclient.IIdentityProvider,
  147. query jsonutils.JSONObject,
  148. input cloudproxy_api.ProxyEndpointCreateInput,
  149. ) (*jsonutils.JSONDict, error) {
  150. data := jsonutils.Marshal(input).(*jsonutils.JSONDict)
  151. if input, err := man.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput); err != nil {
  152. return nil, err
  153. } else {
  154. data.Update(jsonutils.Marshal(input))
  155. }
  156. vs := []validators.IValidator{
  157. validators.NewStringNonEmptyValidator("user").Default("cloudroot"),
  158. validators.NewStringNonEmptyValidator("host"),
  159. validators.NewPortValidator("port").Default(22),
  160. validators.NewSSHKeyValidator("private_key").Optional(true),
  161. validators.NewIPv4AddrValidator("intranet_ip_addr"),
  162. }
  163. for _, v := range vs {
  164. if err := v.Validate(ctx, data); err != nil {
  165. return nil, err
  166. }
  167. }
  168. // populate ssh credential through "cloudhost"
  169. //
  170. // if ! skip validation {
  171. // ssh credential validation
  172. // }
  173. return data, nil
  174. }
  175. func (proxyendpoint *SProxyEndpoint) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  176. err := proxyendpoint.remoteCheckMake(ctx, userCred)
  177. return err
  178. }
  179. func (man *SProxyEndpointManager) getById(id string) (*SProxyEndpoint, error) {
  180. m, err := db.FetchById(man, id)
  181. if err != nil {
  182. return nil, err
  183. }
  184. proxyendpoint := m.(*SProxyEndpoint)
  185. return proxyendpoint, err
  186. }
  187. func (proxyendpoint *SProxyEndpoint) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input cloudproxy_api.ProxyEndpointUpdateInput) (cloudproxy_api.ProxyEndpointUpdateInput, error) {
  188. var err error
  189. input.VirtualResourceBaseUpdateInput, err = proxyendpoint.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.VirtualResourceBaseUpdateInput)
  190. if err != nil {
  191. return input, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData")
  192. }
  193. data := jsonutils.Marshal(input).(*jsonutils.JSONDict)
  194. vs := []validators.IValidator{
  195. validators.NewStringNonEmptyValidator("user"),
  196. validators.NewStringNonEmptyValidator("host"),
  197. validators.NewPortValidator("port"),
  198. validators.NewSSHKeyValidator("private_key").Optional(true),
  199. }
  200. for _, v := range vs {
  201. v.Optional(true)
  202. if err := v.Validate(ctx, data); err != nil {
  203. return input, err
  204. }
  205. }
  206. return input, nil
  207. }
  208. func (proxyendpoint *SProxyEndpoint) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
  209. q := ForwardManager.Query().Equals("proxy_endpoint_id", proxyendpoint.Id)
  210. if count, err := q.CountWithError(); err != nil {
  211. return httperrors.NewServerError("count forwards using proxy endpoint %s(%s)",
  212. proxyendpoint.Name, proxyendpoint.Id)
  213. } else if count > 0 {
  214. return httperrors.NewConflictError("proxy endpoint %s(%s) is still used by %d forward(s)",
  215. proxyendpoint.Name, proxyendpoint.Id, count)
  216. } else {
  217. return nil
  218. }
  219. }
  220. func (proxyendpoint *SProxyEndpoint) PerformPurgeForwards(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  221. q := ForwardManager.Query().Equals("proxy_endpoint_id", proxyendpoint.Id)
  222. forwards := make([]SForward, 0)
  223. err := db.FetchModelObjects(ForwardManager, q, &forwards)
  224. if err != nil {
  225. return nil, errors.Wrapf(err, "unable to fetch all forwards for proxyendpoint %s", proxyendpoint.Id)
  226. }
  227. for i := range forwards {
  228. err := forwards[i].Delete(ctx, userCred)
  229. if err != nil {
  230. return nil, errors.Wrapf(err, "unable to delete forward %s", forwards[i].Id)
  231. }
  232. }
  233. return nil, nil
  234. }
  235. func (proxyendpoint *SProxyEndpoint) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  236. var pms []SProxyMatch
  237. q := ProxyMatchManager.Query().Equals("proxy_endpoint_id", proxyendpoint.Id)
  238. if err := db.FetchModelObjects(ProxyMatchManager, q, &pms); err != nil {
  239. return httperrors.NewServerError("fetch proxy matches for endpoint %s(%s)",
  240. proxyendpoint.Name, proxyendpoint.Id)
  241. }
  242. for i := range pms {
  243. pm := &pms[i]
  244. err := db.DeleteModel(ctx, userCred, pm)
  245. if err != nil {
  246. return err
  247. }
  248. }
  249. return nil
  250. }
  251. func (man *SProxyEndpointManager) ListItemFilter(
  252. ctx context.Context,
  253. q *sqlchemy.SQuery,
  254. userCred mcclient.TokenCredential,
  255. input cloudproxy_api.ProxyEndpointListInput,
  256. ) (*sqlchemy.SQuery, error) {
  257. q, err := man.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, input.VirtualResourceListInput)
  258. if err != nil {
  259. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
  260. }
  261. filters := [][2]string{
  262. [2]string{cloudproxy_api.PM_SCOPE_VPC, input.VpcId},
  263. [2]string{cloudproxy_api.PM_SCOPE_NETWORK, input.NetworkId},
  264. }
  265. for _, filter := range filters {
  266. if v := filter[1]; v != "" {
  267. pmQ := ProxyMatchManager.Query("proxy_endpoint_id").
  268. Equals("match_scope", filter[0]).
  269. Equals("match_value", v)
  270. q = q.In("id", pmQ.SubQuery())
  271. }
  272. }
  273. return q, nil
  274. }