nat_skus.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  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. "strings"
  20. "yunion.io/x/cloudmux/pkg/cloudprovider"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/log"
  23. "yunion.io/x/pkg/errors"
  24. "yunion.io/x/pkg/tristate"
  25. "yunion.io/x/pkg/util/compare"
  26. "yunion.io/x/sqlchemy"
  27. api "yunion.io/x/onecloud/pkg/apis/compute"
  28. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  29. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  30. "yunion.io/x/onecloud/pkg/httperrors"
  31. "yunion.io/x/onecloud/pkg/mcclient"
  32. "yunion.io/x/onecloud/pkg/util/stringutils2"
  33. "yunion.io/x/onecloud/pkg/util/yunionmeta"
  34. )
  35. type SNatSkuManager struct {
  36. db.SEnabledStatusStandaloneResourceBaseManager
  37. db.SExternalizedResourceBaseManager
  38. SCloudregionResourceBaseManager
  39. }
  40. var NatSkuManager *SNatSkuManager
  41. func init() {
  42. NatSkuManager = &SNatSkuManager{
  43. SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager(
  44. SNatSku{},
  45. "nat_skus_tbl",
  46. "nat_sku",
  47. "nat_skus",
  48. ),
  49. }
  50. NatSkuManager.NameRequireAscii = false
  51. NatSkuManager.SetVirtualObject(NatSkuManager)
  52. }
  53. type SNatSku struct {
  54. db.SEnabledStatusStandaloneResourceBase
  55. db.SExternalizedResourceBase
  56. SCloudregionResourceBase
  57. PrepaidStatus string `width:"32" charset:"utf8" nullable:"false" list:"user" create:"admin_optional" update:"admin" default:"available"` // 预付费资源状态 available|soldout
  58. PostpaidStatus string `width:"32" charset:"utf8" nullable:"false" list:"user" create:"admin_optional" update:"admin" default:"available"` // 按需付费资源状态 available|soldout
  59. Provider string `width:"32" charset:"ascii" nullable:"false" list:"user" create:"admin_required" update:"admin"`
  60. ZoneIds string `charset:"utf8" nullable:"true" list:"user" update:"admin" create:"admin_optional" json:"zone_ids"`
  61. }
  62. func (manager *SNatSkuManager) ListItemFilter(
  63. ctx context.Context,
  64. q *sqlchemy.SQuery,
  65. userCred mcclient.TokenCredential,
  66. query api.NatSkuListInput,
  67. ) (*sqlchemy.SQuery, error) {
  68. var err error
  69. q, err = manager.SEnabledStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
  70. if err != nil {
  71. return nil, errors.Wrapf(err, "SEnabledStatusStandaloneResourceBaseManager.ListItemFilter")
  72. }
  73. q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
  74. if err != nil {
  75. return nil, errors.Wrapf(err, "SExternalizedResourceBaseManager.ListItemFilter")
  76. }
  77. q, err = manager.SCloudregionResourceBaseManager.ListItemFilter(ctx, q, userCred, query.RegionalFilterListInput)
  78. if err != nil {
  79. return nil, errors.Wrapf(err, "SCloudregionResourceBaseManager.ListItemFilter")
  80. }
  81. if len(query.PostpaidStatus) > 0 {
  82. q = q.Equals("postpaid_status", query.PostpaidStatus)
  83. }
  84. if len(query.PrepaidStatus) > 0 {
  85. q = q.Equals("prepaid_status", query.PrepaidStatus)
  86. }
  87. if len(query.Providers) > 0 {
  88. q = q.In("provider", query.Providers)
  89. }
  90. return q, nil
  91. }
  92. func (manager *SNatSkuManager) FetchCustomizeColumns(
  93. ctx context.Context,
  94. userCred mcclient.TokenCredential,
  95. query jsonutils.JSONObject,
  96. objs []interface{},
  97. fields stringutils2.SSortedStrings,
  98. isList bool,
  99. ) []api.NatSkuDetails {
  100. rows := make([]api.NatSkuDetails, len(objs))
  101. stdRows := manager.SEnabledStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  102. regRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  103. for i := range rows {
  104. rows[i] = api.NatSkuDetails{
  105. EnabledStatusStandaloneResourceDetails: stdRows[i],
  106. CloudregionResourceInfo: regRows[i],
  107. }
  108. rows[i].CloudEnv = strings.Split(regRows[i].RegionExternalId, "/")[0]
  109. }
  110. return rows
  111. }
  112. func (manager *SNatSkuManager) ListItemExportKeys(ctx context.Context,
  113. q *sqlchemy.SQuery,
  114. userCred mcclient.TokenCredential,
  115. keys stringutils2.SSortedStrings,
  116. ) (*sqlchemy.SQuery, error) {
  117. var err error
  118. q, err = manager.SEnabledStatusStandaloneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  119. if err != nil {
  120. return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.ListItemExportKeys")
  121. }
  122. q, err = manager.SCloudregionResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  123. if err != nil {
  124. return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemExportKeys")
  125. }
  126. return q, nil
  127. }
  128. func (manager *SNatSkuManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  129. var err error
  130. q, err = manager.SEnabledStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
  131. if err == nil {
  132. return q, nil
  133. }
  134. q, err = manager.SCloudregionResourceBaseManager.QueryDistinctExtraField(q, field)
  135. if err == nil {
  136. return q, nil
  137. }
  138. return q, httperrors.ErrNotFound
  139. }
  140. func (manager *SNatSkuManager) OrderByExtraFields(
  141. ctx context.Context,
  142. q *sqlchemy.SQuery,
  143. userCred mcclient.TokenCredential,
  144. query api.NatSkuListInput,
  145. ) (*sqlchemy.SQuery, error) {
  146. var err error
  147. q, err = manager.SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
  148. if err != nil {
  149. return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields")
  150. }
  151. q, err = manager.SCloudregionResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.RegionalFilterListInput)
  152. if err != nil {
  153. return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.OrderByExtraFields")
  154. }
  155. return q, nil
  156. }
  157. func (self *SCloudregion) GetNatSkus() ([]SNatSku, error) {
  158. skus := []SNatSku{}
  159. q := NatSkuManager.Query().Equals("cloudregion_id", self.Id)
  160. err := db.FetchModelObjects(NatSkuManager, q, &skus)
  161. if err != nil {
  162. return nil, errors.Wrapf(err, "db.FetchModelObjects")
  163. }
  164. return skus, nil
  165. }
  166. func (self *SCloudregion) GetElasticcacheSkus() ([]SElasticcacheSku, error) {
  167. skus := []SElasticcacheSku{}
  168. q := ElasticcacheSkuManager.Query().Equals("cloudregion_id", self.Id)
  169. err := db.FetchModelObjects(ElasticcacheSkuManager, q, &skus)
  170. if err != nil {
  171. return nil, errors.Wrapf(err, "db.FetchModelObjects")
  172. }
  173. return skus, nil
  174. }
  175. func (self SNatSku) GetGlobalId() string {
  176. return self.ExternalId
  177. }
  178. func (self *SCloudregion) SyncNatSkus(ctx context.Context, userCred mcclient.TokenCredential, xor bool) compare.SyncResult {
  179. lockman.LockRawObject(ctx, self.Id, NatSkuManager.Keyword())
  180. defer lockman.ReleaseRawObject(ctx, self.Id, NatSkuManager.Keyword())
  181. result := compare.SyncResult{}
  182. meta, err := yunionmeta.FetchYunionmeta(ctx)
  183. if err != nil {
  184. result.Error(errors.Wrapf(err, "FetchYunionmeta"))
  185. return result
  186. }
  187. iskus := []SNatSku{}
  188. err = meta.List(NatSkuManager.Keyword(), self.ExternalId, &iskus)
  189. if err != nil {
  190. result.Error(err)
  191. return result
  192. }
  193. dbSkus, err := self.GetNatSkus()
  194. if err != nil {
  195. result.Error(err)
  196. return result
  197. }
  198. removed := make([]SNatSku, 0)
  199. commondb := make([]SNatSku, 0)
  200. commonext := make([]SNatSku, 0)
  201. added := make([]SNatSku, 0)
  202. err = compare.CompareSets(dbSkus, iskus, &removed, &commondb, &commonext, &added)
  203. if err != nil {
  204. result.Error(err)
  205. return result
  206. }
  207. for i := 0; i < len(removed); i += 1 {
  208. err = removed[i].Delete(ctx, userCred)
  209. if err != nil {
  210. result.DeleteError(err)
  211. continue
  212. }
  213. result.Delete()
  214. }
  215. if !xor {
  216. for i := 0; i < len(commondb); i += 1 {
  217. err = commondb[i].syncWithCloudSku(ctx, userCred, commonext[i])
  218. if err != nil {
  219. result.UpdateError(err)
  220. continue
  221. }
  222. result.Update()
  223. }
  224. }
  225. for i := 0; i < len(added); i += 1 {
  226. err = self.newFromCloudNatSku(ctx, userCred, added[i])
  227. if err != nil {
  228. result.AddError(err)
  229. } else {
  230. result.Add()
  231. }
  232. }
  233. return result
  234. }
  235. func (self *SNatSku) syncWithCloudSku(ctx context.Context, userCred mcclient.TokenCredential, sku SNatSku) error {
  236. _, err := db.Update(self, func() error {
  237. self.PrepaidStatus = sku.PrepaidStatus
  238. self.PostpaidStatus = sku.PostpaidStatus
  239. return nil
  240. })
  241. return err
  242. }
  243. func (self *SCloudregion) newFromCloudNatSku(ctx context.Context, userCred mcclient.TokenCredential, isku SNatSku) error {
  244. meta, err := yunionmeta.FetchYunionmeta(ctx)
  245. if err != nil {
  246. return err
  247. }
  248. zones, err := self.GetZones()
  249. if err != nil {
  250. return errors.Wrap(err, "GetZones")
  251. }
  252. zoneMaps := map[string]string{}
  253. for _, zone := range zones {
  254. zoneMaps[zone.ExternalId] = zone.Id
  255. }
  256. sku := &SNatSku{}
  257. sku.SetModelManager(NatSkuManager, sku)
  258. skuUrl := self.getMetaUrl(meta.NatBase, isku.GetGlobalId())
  259. err = meta.Get(skuUrl, sku)
  260. if err != nil {
  261. return errors.Wrapf(err, "Get")
  262. }
  263. if len(sku.ZoneIds) > 0 {
  264. zoneIds := []string{}
  265. for _, zoneExtId := range strings.Split(sku.ZoneIds, ",") {
  266. zoneId := yunionmeta.GetZoneIdBySuffix(zoneMaps, zoneExtId) // Huawei rds sku zone1 maybe is cn-north-4f
  267. if len(zoneId) > 0 {
  268. zoneIds = append(zoneIds, zoneId)
  269. }
  270. }
  271. sku.ZoneIds = strings.Join(zoneIds, ",")
  272. }
  273. sku.Status = api.NAS_SKU_AVAILABLE
  274. sku.SetEnabled(true)
  275. sku.CloudregionId = self.Id
  276. sku.Provider = self.Provider
  277. return NatSkuManager.TableSpec().Insert(ctx, sku)
  278. }
  279. func SyncNatSkus(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
  280. err := SyncRegionNatSkus(ctx, userCred, "", isStart, false)
  281. if err != nil {
  282. log.Errorf("SyncRegionNatSkus error: %v", err)
  283. }
  284. }
  285. func SyncRegionNatSkus(ctx context.Context, userCred mcclient.TokenCredential, regionId string, isStart, xor bool) error {
  286. if isStart {
  287. q := NatSkuManager.Query()
  288. if len(regionId) > 0 {
  289. q = q.Equals("cloudregion_id", regionId)
  290. }
  291. cnt, err := q.Limit(1).CountWithError()
  292. if err != nil && err != sql.ErrNoRows {
  293. return errors.Wrapf(err, "SyncRegionNatSkus.QueryNatSku")
  294. }
  295. if cnt > 0 {
  296. log.Debugf("SyncRegionNatSkus synced skus, skip...")
  297. return nil
  298. }
  299. }
  300. q := CloudregionManager.Query()
  301. q = q.In("provider", CloudproviderManager.GetPublicProviderProvidersQuery())
  302. if len(regionId) > 0 {
  303. q = q.Equals("id", regionId)
  304. }
  305. regions := []SCloudregion{}
  306. err := db.FetchModelObjects(CloudregionManager, q, &regions)
  307. if err != nil {
  308. return errors.Wrapf(err, "db.FetchModelObjects")
  309. }
  310. if len(regions) == 0 {
  311. return nil
  312. }
  313. meta, err := yunionmeta.FetchYunionmeta(ctx)
  314. if err != nil {
  315. return errors.Wrapf(err, "FetchYunionmeta")
  316. }
  317. index, err := meta.Index(NatSkuManager.Keyword())
  318. if err != nil {
  319. log.Errorf("get nat sku index error: %v", err)
  320. return err
  321. }
  322. for i := range regions {
  323. region := regions[i]
  324. if !region.GetDriver().IsSupportedNatGateway() {
  325. log.Infof("region %s(%s) not support nat, skip sync", regions[i].Name, regions[i].Id)
  326. continue
  327. }
  328. skuMeta := &SNatSku{}
  329. skuMeta.SetModelManager(NatSkuManager, skuMeta)
  330. skuMeta.Id = region.ExternalId
  331. oldMd5 := db.Metadata.GetStringValue(ctx, skuMeta, db.SKU_METADAT_KEY, userCred)
  332. newMd5, ok := index[region.ExternalId]
  333. if !ok || newMd5 == yunionmeta.EMPTY_MD5 || len(oldMd5) > 0 && newMd5 == oldMd5 {
  334. continue
  335. }
  336. db.Metadata.SetValue(ctx, skuMeta, db.SKU_METADAT_KEY, newMd5, userCred)
  337. result := regions[i].SyncNatSkus(ctx, userCred, xor)
  338. notes := fmt.Sprintf("SyncNatSkus for region %s result: %v", regions[i].Name, result.Result())
  339. log.Infof("%s", notes)
  340. }
  341. return nil
  342. }
  343. func (manager *SNatSkuManager) PerformSyncSkus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SkuSyncInput) (jsonutils.JSONObject, error) {
  344. return PerformActionSyncSkus(ctx, userCred, manager.Keyword(), input)
  345. }
  346. func (manager *SNatSkuManager) GetPropertySyncTasks(ctx context.Context, userCred mcclient.TokenCredential, query api.SkuTaskQueryInput) (jsonutils.JSONObject, error) {
  347. return GetPropertySkusSyncTasks(ctx, userCred, query)
  348. }
  349. func (self *SCloudregion) SyncPrivateCloudNatSkus(ctx context.Context, userCred mcclient.TokenCredential, iskus []cloudprovider.ICloudNatSku) compare.SyncResult {
  350. lockman.LockRawObject(ctx, self.Id, NatSkuManager.Keyword())
  351. defer lockman.ReleaseRawObject(ctx, self.Id, NatSkuManager.Keyword())
  352. result := compare.SyncResult{}
  353. dbSkus, err := self.GetNatSkus()
  354. if err != nil {
  355. result.Error(err)
  356. return result
  357. }
  358. removed := make([]SNatSku, 0)
  359. commondb := make([]SNatSku, 0)
  360. commonext := make([]cloudprovider.ICloudNatSku, 0)
  361. added := make([]cloudprovider.ICloudNatSku, 0)
  362. err = compare.CompareSets(dbSkus, iskus, &removed, &commondb, &commonext, &added)
  363. if err != nil {
  364. result.Error(err)
  365. return result
  366. }
  367. for i := 0; i < len(removed); i += 1 {
  368. err = removed[i].Delete(ctx, userCred)
  369. if err != nil {
  370. result.DeleteError(err)
  371. continue
  372. }
  373. result.Delete()
  374. }
  375. for i := 0; i < len(added); i += 1 {
  376. err = self.newFromPrivateCloudNatSku(ctx, userCred, added[i])
  377. if err != nil {
  378. result.AddError(err)
  379. } else {
  380. result.Add()
  381. }
  382. }
  383. return result
  384. }
  385. func (self *SCloudregion) newFromPrivateCloudNatSku(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudNatSku) error {
  386. sku := &SNasSku{}
  387. sku.SetModelManager(NatSkuManager, sku)
  388. sku.Name = ext.GetName()
  389. sku.Description = ext.GetDesc()
  390. sku.ExternalId = ext.GetGlobalId()
  391. sku.CloudregionId = self.Id
  392. sku.Provider = self.Provider
  393. sku.PrepaidStatus = ext.GetPrepaidStatus()
  394. sku.PostpaidStatus = ext.GetPostpaidStatus()
  395. sku.Enabled = tristate.True
  396. sku.Status = api.NAT_SKU_AVAILABLE
  397. return NatSkuManager.TableSpec().Insert(ctx, sku)
  398. }