loadbalancerbackends.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  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. "database/sql"
  18. "fmt"
  19. "yunion.io/x/cloudmux/pkg/cloudprovider"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. "yunion.io/x/pkg/util/compare"
  24. "yunion.io/x/pkg/util/netutils"
  25. "yunion.io/x/pkg/util/rand"
  26. "yunion.io/x/pkg/util/rbacscope"
  27. "yunion.io/x/pkg/utils"
  28. "yunion.io/x/sqlchemy"
  29. api "yunion.io/x/onecloud/pkg/apis/compute"
  30. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  31. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  32. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  33. "yunion.io/x/onecloud/pkg/cloudcommon/validators"
  34. "yunion.io/x/onecloud/pkg/httperrors"
  35. "yunion.io/x/onecloud/pkg/mcclient"
  36. "yunion.io/x/onecloud/pkg/util/stringutils2"
  37. )
  38. // +onecloud:swagger-gen-model-singular=loadbalancerbackend
  39. // +onecloud:swagger-gen-model-plural=loadbalancerbackends
  40. type SLoadbalancerBackendManager struct {
  41. SLoadbalancerLogSkipper
  42. db.SStatusStandaloneResourceBaseManager
  43. db.SExternalizedResourceBaseManager
  44. SLoadbalancerBackendgroupResourceBaseManager
  45. }
  46. var LoadbalancerBackendManager *SLoadbalancerBackendManager
  47. func init() {
  48. LoadbalancerBackendManager = &SLoadbalancerBackendManager{
  49. SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager(
  50. SLoadbalancerBackend{},
  51. "loadbalancerbackends_tbl",
  52. "loadbalancerbackend",
  53. "loadbalancerbackends",
  54. ),
  55. }
  56. LoadbalancerBackendManager.SetVirtualObject(LoadbalancerBackendManager)
  57. }
  58. type SLoadbalancerBackend struct {
  59. db.SStatusStandaloneResourceBase
  60. db.SExternalizedResourceBase
  61. SLoadbalancerBackendgroupResourceBase `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"`
  62. BackendId string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"`
  63. BackendType string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"`
  64. BackendRole string `width:"36" charset:"ascii" nullable:"false" list:"user" default:"default" create:"optional"`
  65. Weight int `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
  66. Address string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"`
  67. Port int `nullable:"false" list:"user" create:"required" update:"user"`
  68. SendProxy string `width:"16" charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user" default:"off"`
  69. Ssl string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user" default:"off"`
  70. }
  71. func (manager *SLoadbalancerBackendManager) ResourceScope() rbacscope.TRbacScope {
  72. return rbacscope.ScopeProject
  73. }
  74. func (self *SLoadbalancerBackend) GetOwnerId() mcclient.IIdentityProvider {
  75. lbbg, err := self.GetLoadbalancerBackendGroup()
  76. if err != nil {
  77. return nil
  78. }
  79. return lbbg.GetOwnerId()
  80. }
  81. func (manager *SLoadbalancerBackendManager) FetchOwnerId(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) {
  82. lbbgId, _ := data.GetString("backend_group_id")
  83. if len(lbbgId) > 0 {
  84. lbbg, err := db.FetchById(LoadbalancerBackendGroupManager, lbbgId)
  85. if err != nil {
  86. return nil, errors.Wrapf(err, "db.FetchById(LoadbalancerBackendGroupManager, %s)", lbbgId)
  87. }
  88. return lbbg.(*SLoadbalancerBackendGroup).GetOwnerId(), nil
  89. }
  90. return db.FetchProjectInfo(ctx, data)
  91. }
  92. func (man *SLoadbalancerBackendManager) FilterByOwner(ctx context.Context, q *sqlchemy.SQuery, manager db.FilterByOwnerProvider, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope) *sqlchemy.SQuery {
  93. if ownerId != nil {
  94. sq := LoadbalancerBackendGroupManager.Query("id")
  95. lb := LoadbalancerManager.Query().SubQuery()
  96. sq = sq.Join(lb, sqlchemy.Equals(sq.Field("loadbalancer_id"), lb.Field("id")))
  97. switch scope {
  98. case rbacscope.ScopeProject:
  99. sq = sq.Filter(sqlchemy.Equals(lb.Field("tenant_id"), ownerId.GetProjectId()))
  100. return q.In("backend_group_id", sq.SubQuery())
  101. case rbacscope.ScopeDomain:
  102. sq = sq.Filter(sqlchemy.Equals(lb.Field("domain_id"), ownerId.GetProjectDomainId()))
  103. return q.In("backend_group_id", sq.SubQuery())
  104. }
  105. }
  106. return q
  107. }
  108. // 负载均衡后端列表
  109. func (man *SLoadbalancerBackendManager) ListItemFilter(
  110. ctx context.Context,
  111. q *sqlchemy.SQuery,
  112. userCred mcclient.TokenCredential,
  113. query api.LoadbalancerBackendListInput,
  114. ) (*sqlchemy.SQuery, error) {
  115. q, err := man.SStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StatusStandaloneResourceListInput)
  116. if err != nil {
  117. return nil, errors.Wrap(err, "SStatusStandaloneResourceBaseManager.ListItemFilter")
  118. }
  119. q, err = man.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
  120. if err != nil {
  121. return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
  122. }
  123. q, err = man.SLoadbalancerBackendgroupResourceBaseManager.ListItemFilter(ctx, q, userCred, query.LoadbalancerBackendGroupFilterListInput)
  124. if err != nil {
  125. return nil, errors.Wrap(err, "SLoadbalancerBackendgroupResourceBaseManager.ListItemFilter")
  126. }
  127. data := jsonutils.Marshal(query).(*jsonutils.JSONDict)
  128. q, err = validators.ApplyModelFilters(ctx, q, data, []*validators.ModelFilterOptions{
  129. {Key: "backend", ModelKeyword: "server", OwnerId: userCred}, // NOTE extend this when new backend_type was added
  130. })
  131. if err != nil {
  132. return nil, err
  133. }
  134. if len(query.BackendType) > 0 {
  135. q = q.In("backend_type", query.BackendType)
  136. }
  137. if len(query.BackendRole) > 0 {
  138. q = q.In("backend_role", query.BackendRole)
  139. }
  140. if len(query.Address) > 0 {
  141. q = q.In("address", query.Address)
  142. }
  143. if len(query.SendProxy) > 0 {
  144. q = q.In("send_proxy", query.SendProxy)
  145. }
  146. if len(query.Ssl) > 0 {
  147. q = q.In("ssl", query.Ssl)
  148. }
  149. return q, nil
  150. }
  151. func (man *SLoadbalancerBackendManager) OrderByExtraFields(
  152. ctx context.Context,
  153. q *sqlchemy.SQuery,
  154. userCred mcclient.TokenCredential,
  155. query api.LoadbalancerBackendListInput,
  156. ) (*sqlchemy.SQuery, error) {
  157. var err error
  158. q, err = man.SStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusStandaloneResourceListInput)
  159. if err != nil {
  160. return nil, errors.Wrap(err, "SStatusStandaloneResourceBaseManager.OrderByExtraFields")
  161. }
  162. q, err = man.SLoadbalancerBackendgroupResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.LoadbalancerBackendGroupFilterListInput)
  163. if err != nil {
  164. return nil, errors.Wrap(err, "SLoadbalancerBackendgroupResourceBaseManager.OrderByExtraFields")
  165. }
  166. return q, nil
  167. }
  168. func (man *SLoadbalancerBackendManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  169. var err error
  170. q, err = man.SStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
  171. if err == nil {
  172. return q, nil
  173. }
  174. q, err = man.SLoadbalancerBackendgroupResourceBaseManager.QueryDistinctExtraField(q, field)
  175. if err == nil {
  176. return q, nil
  177. }
  178. return q, httperrors.ErrNotFound
  179. }
  180. func (man *SLoadbalancerBackendManager) ValidateBackendVpc(lb *SLoadbalancer, guest *SGuest, backendgroup *SLoadbalancerBackendGroup) error {
  181. region, err := lb.GetRegion()
  182. if err != nil {
  183. return err
  184. }
  185. requireStatus := region.GetDriver().GetBackendStatusForAdd()
  186. if !utils.IsInStringArray(guest.Status, requireStatus) {
  187. return httperrors.NewUnsupportOperationError("%s requires the virtual machine state to be %s before it can be added backendgroup, but current state of the virtual machine is %s", region.GetDriver().GetProvider(), requireStatus, guest.Status)
  188. }
  189. vpc, err := guest.GetVpc()
  190. if err != nil {
  191. return httperrors.NewBadRequestError("%s", err)
  192. }
  193. if len(lb.VpcId) > 0 {
  194. lbVpc, err := lb.GetVpc()
  195. if err != nil {
  196. return err
  197. }
  198. if lbVpc != nil && !lbVpc.IsEmulated && vpc.Id != lb.VpcId {
  199. return httperrors.NewBadRequestError("guest %s(%s) vpc %s(%s) not same as loadbalancer vpc %s", guest.Name, guest.Id, vpc.Name, vpc.Id, lb.VpcId)
  200. }
  201. return nil
  202. }
  203. backends, err := backendgroup.GetBackends()
  204. if err != nil {
  205. return err
  206. }
  207. for _, backend := range backends {
  208. _server, err := GuestManager.FetchById(backend.BackendId)
  209. if err != nil {
  210. return httperrors.NewBadRequestError("failed getting guest %s", backend.BackendId)
  211. }
  212. server := _server.(*SGuest)
  213. _vpc, err := server.GetVpc()
  214. if err != nil {
  215. return httperrors.NewBadRequestError("%s", err)
  216. }
  217. if _vpc.Id != vpc.Id {
  218. return httperrors.NewBadRequestError("guest %s(%s) vpc %s(%s) not same as vpc %s(%s)", guest.Name, guest.Id, vpc.Name, vpc.Id, _vpc.Name, _vpc.Id)
  219. }
  220. if _server.GetId() == guest.Id {
  221. return httperrors.NewBadRequestError("guest %s(%s) is already in the backendgroup %s(%s)", guest.Name, guest.Id, backendgroup.Name, backendgroup.Id)
  222. }
  223. }
  224. return nil
  225. }
  226. func (man *SLoadbalancerBackendManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential,
  227. ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject,
  228. input *api.LoadbalancerBackendCreateInput) (*api.LoadbalancerBackendCreateInput, error) {
  229. lbbgObj, err := validators.ValidateModel(ctx, userCred, LoadbalancerBackendGroupManager, &input.BackendGroupId)
  230. if err != nil {
  231. return nil, err
  232. }
  233. lbbg := lbbgObj.(*SLoadbalancerBackendGroup)
  234. if input.Port < 1 || input.Port > 65535 {
  235. return input, httperrors.NewInputParameterError("invalid port %d", input.Port)
  236. }
  237. if input.Weight < 0 || input.Weight > 100 {
  238. return input, httperrors.NewInputParameterError("invalid weight %d", input.Weight)
  239. }
  240. if len(input.SendProxy) == 0 {
  241. input.SendProxy = api.LB_SENDPROXY_OFF
  242. }
  243. if !utils.IsInStringArray(input.SendProxy, api.LB_SENDPROXY_CHOICES) {
  244. return input, httperrors.NewInputParameterError("invalid send_proxy %s", input.SendProxy)
  245. }
  246. if len(input.Ssl) > 0 && !utils.IsInStringArray(input.Ssl, []string{api.LB_BOOL_ON, api.LB_BOOL_OFF}) {
  247. return input, httperrors.NewInputParameterError("invalid ssl %s", input.Ssl)
  248. }
  249. lb, err := lbbg.GetLoadbalancer()
  250. if err != nil {
  251. return nil, errors.Wrapf(err, "GetLoadbalancer")
  252. }
  253. if len(input.BackendId) == 0 && input.BackendType != api.LB_BACKEND_ADDRESS {
  254. return nil, httperrors.NewMissingParameterError("backend_id")
  255. }
  256. region, err := lb.GetRegion()
  257. if err != nil {
  258. return nil, err
  259. }
  260. baseName := ""
  261. switch input.BackendType {
  262. case api.LB_BACKEND_GUEST:
  263. guestObj, err := validators.ValidateModel(ctx, userCred, GuestManager, &input.BackendId)
  264. if err != nil {
  265. return nil, err
  266. }
  267. guest := guestObj.(*SGuest)
  268. input.Address, err = guest.GetAddress()
  269. if err != nil {
  270. return nil, err
  271. }
  272. baseName = guest.Name
  273. host, err := guest.GetHost()
  274. if err != nil {
  275. return nil, errors.Wrapf(err, "GetHost")
  276. }
  277. hRegion, err := host.GetRegion()
  278. if err != nil {
  279. return nil, err
  280. }
  281. if hRegion.Id != region.Id {
  282. return nil, httperrors.NewInputParameterError("region of host %q (%s) != region of loadbalancer %q (%s))",
  283. host.Name, host.ZoneId, lb.Name, lb.ZoneId)
  284. }
  285. if len(lb.ManagerId) == 0 {
  286. if !utils.IsInStringArray(host.HostType, []string{api.HOST_TYPE_HYPERVISOR, api.HOST_TYPE_ESXI, api.HOST_TYPE_BAREMETAL}) {
  287. return nil, httperrors.NewInputParameterError("host type of host %q (%s) should be either hypervisor, baremetal or esxi",
  288. host.Name, host.HostType)
  289. }
  290. } else if host.ManagerId != lb.ManagerId {
  291. return nil, httperrors.NewInputParameterError("manager of host %q (%s) != manager of loadbalancer %q (%s))",
  292. host.Name, host.ManagerId, lb.Name, lb.ManagerId)
  293. }
  294. case api.LB_BACKEND_HOST:
  295. hostObj, err := validators.ValidateModel(ctx, userCred, HostManager, &input.BackendId)
  296. if err != nil {
  297. return nil, err
  298. }
  299. host := hostObj.(*SHost)
  300. if len(host.AccessIp) == 0 {
  301. return nil, fmt.Errorf("host %s has no access ip", host.GetId())
  302. }
  303. hRegion, err := host.GetRegion()
  304. if err != nil {
  305. return nil, err
  306. }
  307. if hRegion.Id != region.Id {
  308. return nil, httperrors.NewInputParameterError("region of host %q (%s) != region of loadbalancer %q (%s))",
  309. host.Name, host.ZoneId, lb.Name, lb.ZoneId)
  310. }
  311. if host.ManagerId != lb.ManagerId {
  312. return nil, httperrors.NewInputParameterError("manager of host %q (%s) != manager of loadbalancer %q (%s))",
  313. host.Name, host.ManagerId, lb.Name, lb.ManagerId)
  314. }
  315. input.Address = host.AccessIp
  316. baseName = host.Name
  317. case api.LB_BACKEND_IP:
  318. _, err := netutils.NewIPV4Addr(input.BackendId)
  319. if err != nil {
  320. return nil, err
  321. }
  322. input.Address = input.BackendId
  323. baseName = input.Address
  324. case api.LB_BACKEND_ADDRESS:
  325. if len(input.Address) == 0 {
  326. return nil, httperrors.NewMissingParameterError("address")
  327. }
  328. default:
  329. return input, httperrors.NewInputParameterError("invalid backend_type %s", input.BackendType)
  330. }
  331. if len(input.Name) == 0 {
  332. input.Name = fmt.Sprintf("%s-%s-%s-%s", lbbg.Name, input.BackendType, baseName, rand.String(4))
  333. }
  334. input.StatusStandaloneResourceCreateInput, err = man.SStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StatusStandaloneResourceCreateInput)
  335. if err != nil {
  336. return nil, err
  337. }
  338. return region.GetDriver().ValidateCreateLoadbalancerBackendData(ctx, userCred, lb, lbbg, input)
  339. }
  340. func (lbb *SLoadbalancerBackend) GetCloudproviderId() string {
  341. lbbg, _ := lbb.GetLoadbalancerBackendGroup()
  342. if lbbg != nil {
  343. return lbbg.GetCloudproviderId()
  344. }
  345. return ""
  346. }
  347. func (lbb *SLoadbalancerBackend) GetLoadbalancerBackendGroup() (*SLoadbalancerBackendGroup, error) {
  348. backendgroup, err := LoadbalancerBackendGroupManager.FetchById(lbb.BackendGroupId)
  349. if err != nil {
  350. return nil, errors.Wrapf(err, "GetLoadbalancerBackendGroup(%s)", lbb.BackendGroupId)
  351. }
  352. return backendgroup.(*SLoadbalancerBackendGroup), nil
  353. }
  354. func (lbb *SLoadbalancerBackend) GetGuest() *SGuest {
  355. guest, err := GuestManager.FetchById(lbb.BackendId)
  356. if err != nil {
  357. return nil
  358. }
  359. return guest.(*SGuest)
  360. }
  361. func (lbb *SLoadbalancerBackend) GetRegion() (*SCloudregion, error) {
  362. backendgroup, err := lbb.GetLoadbalancerBackendGroup()
  363. if err != nil {
  364. return nil, err
  365. }
  366. return backendgroup.GetRegion()
  367. }
  368. func (lbb *SLoadbalancerBackend) GetIRegion(ctx context.Context) (cloudprovider.ICloudRegion, error) {
  369. backendgroup, err := lbb.GetLoadbalancerBackendGroup()
  370. if err != nil {
  371. return nil, err
  372. }
  373. return backendgroup.GetIRegion(ctx)
  374. }
  375. func (lbb *SLoadbalancerBackend) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.LoadbalancerBackendUpdateInput) (*api.LoadbalancerBackendUpdateInput, error) {
  376. var err error
  377. input.StatusStandaloneResourceBaseUpdateInput, err = lbb.SStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StatusStandaloneResourceBaseUpdateInput)
  378. if err != nil {
  379. return nil, errors.Wrap(err, "SStatusStandaloneResourceBase.ValidateUpdateData")
  380. }
  381. region, err := lbb.GetRegion()
  382. if err != nil {
  383. return nil, err
  384. }
  385. lbbg, err := lbb.GetLoadbalancerBackendGroup()
  386. if err != nil {
  387. return nil, err
  388. }
  389. return region.GetDriver().ValidateUpdateLoadbalancerBackendData(ctx, userCred, lbbg, input)
  390. }
  391. func (lbb *SLoadbalancerBackend) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  392. lbb.SStatusStandaloneResourceBase.PostUpdate(ctx, userCred, query, data)
  393. if data.Contains("port") || data.Contains("weight") || data.Contains("enabled") {
  394. params := data.(*jsonutils.JSONDict)
  395. if !params.Contains("enabled") {
  396. params.Add(jsonutils.NewBool(lbb.Status == api.LB_STATUS_ENABLED), "enabled")
  397. }
  398. lbb.StartLoadBalancerBackendSyncTask(ctx, userCred, params, "")
  399. }
  400. }
  401. func (lbb *SLoadbalancerBackend) StartLoadBalancerBackendSyncTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
  402. lbb.SetStatus(ctx, userCred, api.LB_SYNC_CONF, "")
  403. task, err := taskman.TaskManager.NewTask(ctx, "LoadbalancerBackendSyncTask", lbb, userCred, params, parentTaskId, "", nil)
  404. if err != nil {
  405. return err
  406. }
  407. return task.ScheduleRun(nil)
  408. }
  409. func (lbb *SLoadbalancerBackend) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  410. lbb.SStatusStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
  411. lbb.SetStatus(ctx, userCred, api.LB_CREATING, "")
  412. err := lbb.StartLoadBalancerBackendCreateTask(ctx, userCred, "")
  413. if err != nil {
  414. log.Errorf("Failed to create loadbalancer backend error: %v", err)
  415. }
  416. }
  417. func (lbb *SLoadbalancerBackend) getVpc(ctx context.Context) (*SVpc, error) {
  418. if lbb.BackendType != api.LB_BACKEND_GUEST {
  419. return nil, nil
  420. }
  421. guestM, err := GuestManager.FetchById(lbb.BackendId)
  422. if err != nil {
  423. return nil, errors.Wrapf(err, "find guest %s", lbb.BackendId)
  424. }
  425. guest := guestM.(*SGuest)
  426. return guest.GetVpc()
  427. }
  428. func (manager *SLoadbalancerBackendManager) FetchCustomizeColumns(
  429. ctx context.Context,
  430. userCred mcclient.TokenCredential,
  431. query jsonutils.JSONObject,
  432. objs []interface{},
  433. fields stringutils2.SSortedStrings,
  434. isList bool,
  435. ) []api.LoadbalancerBackendDetails {
  436. rows := make([]api.LoadbalancerBackendDetails, len(objs))
  437. stdRows := manager.SStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  438. lbbgRows := manager.SLoadbalancerBackendgroupResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  439. lbIds := make([]string, len(objs))
  440. for i := range rows {
  441. rows[i] = api.LoadbalancerBackendDetails{
  442. StatusStandaloneResourceDetails: stdRows[i],
  443. LoadbalancerBackendGroupResourceInfo: lbbgRows[i],
  444. }
  445. lbIds[i] = rows[i].LoadbalancerId
  446. }
  447. lbs := map[string]SLoadbalancer{}
  448. err := db.FetchStandaloneObjectsByIds(LoadbalancerManager, lbIds, &lbs)
  449. if err != nil {
  450. return rows
  451. }
  452. virObjs := make([]interface{}, len(objs))
  453. for i := range rows {
  454. if lb, ok := lbs[lbIds[i]]; ok {
  455. virObjs[i] = &lb
  456. rows[i].ProjectId = lb.ProjectId
  457. }
  458. }
  459. return rows
  460. }
  461. func (lbb *SLoadbalancerBackend) StartLoadBalancerBackendCreateTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  462. task, err := taskman.TaskManager.NewTask(ctx, "LoadbalancerBackendCreateTask", lbb, userCred, nil, parentTaskId, "", nil)
  463. if err != nil {
  464. return errors.Wrapf(err, "NewTask")
  465. }
  466. return task.ScheduleRun(nil)
  467. }
  468. func (lbb *SLoadbalancerBackend) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
  469. return nil
  470. }
  471. func (self *SLoadbalancerBackend) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  472. return self.SStatusStandaloneResourceBase.Delete(ctx, userCred)
  473. }
  474. func (lbb *SLoadbalancerBackend) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  475. parasm := jsonutils.NewDict()
  476. parasm.Add(jsonutils.JSONTrue, "purge")
  477. return nil, lbb.StartLoadBalancerBackendDeleteTask(ctx, userCred, parasm, "")
  478. }
  479. func (lbb *SLoadbalancerBackend) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  480. lbb.SetStatus(ctx, userCred, api.LB_STATUS_DELETING, "")
  481. return lbb.StartLoadBalancerBackendDeleteTask(ctx, userCred, jsonutils.NewDict(), "")
  482. }
  483. func (lbb *SLoadbalancerBackend) StartLoadBalancerBackendDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
  484. task, err := taskman.TaskManager.NewTask(ctx, "LoadbalancerBackendDeleteTask", lbb, userCred, params, parentTaskId, "", nil)
  485. if err != nil {
  486. return err
  487. }
  488. task.ScheduleRun(nil)
  489. return nil
  490. }
  491. func (lbb *SLoadbalancerBackend) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
  492. return lbb.SStatusStandaloneResourceBase.ValidateDeleteCondition(ctx, nil)
  493. }
  494. func (lbbg *SLoadbalancerBackendGroup) SyncLoadbalancerBackends(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, exts []cloudprovider.ICloudLoadbalancerBackend) compare.SyncResult {
  495. lockman.LockRawObject(ctx, LoadbalancerBackendManager.Keyword(), lbbg.Id)
  496. defer lockman.ReleaseRawObject(ctx, LoadbalancerBackendManager.Keyword(), lbbg.Id)
  497. result := compare.SyncResult{}
  498. dbRes, err := lbbg.GetBackends()
  499. if err != nil {
  500. result.Error(err)
  501. return result
  502. }
  503. removed := []SLoadbalancerBackend{}
  504. commondb := []SLoadbalancerBackend{}
  505. commonext := []cloudprovider.ICloudLoadbalancerBackend{}
  506. added := []cloudprovider.ICloudLoadbalancerBackend{}
  507. err = compare.CompareSets(dbRes, exts, &removed, &commondb, &commonext, &added)
  508. if err != nil {
  509. result.Error(err)
  510. return result
  511. }
  512. for i := 0; i < len(removed); i++ {
  513. err = removed[i].syncRemove(ctx, userCred)
  514. if err != nil {
  515. result.DeleteError(err)
  516. continue
  517. }
  518. result.Delete()
  519. }
  520. for i := 0; i < len(commondb); i++ {
  521. err = commondb[i].SyncWithCloudLoadbalancerBackend(ctx, userCred, commonext[i], provider)
  522. if err != nil {
  523. result.UpdateError(err)
  524. continue
  525. }
  526. result.Update()
  527. }
  528. for i := 0; i < len(added); i++ {
  529. _, err := lbbg.newFromCloudLoadbalancerBackend(ctx, userCred, added[i], provider)
  530. if err != nil {
  531. result.AddError(err)
  532. continue
  533. }
  534. result.Add()
  535. }
  536. return result
  537. }
  538. func (lbb *SLoadbalancerBackend) constructFieldsFromCloudLoadbalancerBackend(ext cloudprovider.ICloudLoadbalancerBackend, managerId string) error {
  539. lbb.Status = ext.GetStatus()
  540. lbb.Weight = ext.GetWeight()
  541. lbb.Port = ext.GetPort()
  542. lbb.BackendType = ext.GetBackendType()
  543. lbb.BackendRole = ext.GetBackendRole()
  544. ipAddr := ext.GetIpAddress()
  545. if len(lbb.Address) == 0 || ipAddr != lbb.Address {
  546. lbb.Address = ipAddr
  547. }
  548. if lbb.BackendType == api.LB_BACKEND_GUEST {
  549. instance, err := db.FetchByExternalIdAndManagerId(GuestManager, ext.GetBackendId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  550. sq := HostManager.Query().SubQuery()
  551. return q.Join(sq, sqlchemy.Equals(sq.Field("id"), q.Field("host_id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), managerId))
  552. })
  553. if err != nil {
  554. // 部分弹性伸缩组实例未同步, 忽略找不到实例错误
  555. if errors.Cause(err) == sql.ErrNoRows {
  556. return nil
  557. }
  558. return errors.Wrapf(err, "FetchByExternalIdAndManagerId %s", ext.GetBackendId())
  559. }
  560. guest := instance.(*SGuest)
  561. lbb.BackendId = guest.Id
  562. if len(lbb.Address) == 0 {
  563. lbb.Address, _ = guest.GetAddress()
  564. }
  565. }
  566. return nil
  567. }
  568. func (lbb *SLoadbalancerBackend) syncRemove(ctx context.Context, userCred mcclient.TokenCredential) error {
  569. lockman.LockObject(ctx, lbb)
  570. defer lockman.ReleaseObject(ctx, lbb)
  571. err := lbb.ValidateDeleteCondition(ctx, nil)
  572. if err != nil { // cannot delete
  573. lbb.SetStatus(ctx, userCred, api.LB_STATUS_UNKNOWN, "sync to delete")
  574. return errors.Wrapf(err, "ValidateDeleteCondition")
  575. }
  576. return lbb.RealDelete(ctx, userCred)
  577. }
  578. func (lbb *SLoadbalancerBackend) SyncWithCloudLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudLoadbalancerBackend, provider *SCloudprovider) error {
  579. diff, err := db.UpdateWithLock(ctx, lbb, func() error {
  580. return lbb.constructFieldsFromCloudLoadbalancerBackend(ext, provider.Id)
  581. })
  582. if err != nil {
  583. return err
  584. }
  585. db.OpsLog.LogSyncUpdate(lbb, diff, userCred)
  586. return nil
  587. }
  588. func (lbbg *SLoadbalancerBackendGroup) newFromCloudLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudLoadbalancerBackend, provider *SCloudprovider) (*SLoadbalancerBackend, error) {
  589. lbb := &SLoadbalancerBackend{}
  590. lbb.SetModelManager(LoadbalancerBackendManager, lbb)
  591. lbb.BackendGroupId = lbbg.Id
  592. lbb.ExternalId = ext.GetGlobalId()
  593. err := lbb.constructFieldsFromCloudLoadbalancerBackend(ext, provider.Id)
  594. if err != nil {
  595. return nil, errors.Wrapf(err, "constructFieldsFromCloudLoadbalancerBackend")
  596. }
  597. lbb.Name = ext.GetName()
  598. err = LoadbalancerBackendManager.TableSpec().Insert(ctx, lbb)
  599. if err != nil {
  600. return nil, errors.Wrapf(err, "Insert")
  601. }
  602. db.OpsLog.LogEvent(lbb, db.ACT_CREATE, lbb.GetShortDesc(ctx), userCred)
  603. return lbb, nil
  604. }
  605. func (manager *SLoadbalancerBackendManager) ListItemExportKeys(ctx context.Context,
  606. q *sqlchemy.SQuery,
  607. userCred mcclient.TokenCredential,
  608. keys stringutils2.SSortedStrings,
  609. ) (*sqlchemy.SQuery, error) {
  610. var err error
  611. q, err = manager.SStatusStandaloneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  612. if err != nil {
  613. return nil, errors.Wrap(err, "SStatusStandaloneResourceBaseManager.ListItemExportKeys")
  614. }
  615. if keys.ContainsAny(manager.SLoadbalancerBackendgroupResourceBaseManager.GetExportKeys()...) {
  616. q, err = manager.SLoadbalancerBackendgroupResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  617. if err != nil {
  618. return nil, errors.Wrap(err, "SLoadbalancerBackendgroupResourceBaseManager.ListItemExportKeys")
  619. }
  620. }
  621. return q, nil
  622. }
  623. func (manager *SLoadbalancerBackendManager) InitializeData() error {
  624. return nil
  625. }