sku.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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 google
  15. import (
  16. "fmt"
  17. "strings"
  18. "time"
  19. "yunion.io/x/log"
  20. "yunion.io/x/pkg/errors"
  21. "yunion.io/x/pkg/utils"
  22. api "yunion.io/x/cloudmux/pkg/apis/compute"
  23. )
  24. type SkuBillingCatetory struct {
  25. ServiceDisplayName string
  26. ResourceFamily string
  27. ResourceGroup string
  28. UsageType string
  29. }
  30. type SkuPricingInfo struct {
  31. Summary string
  32. PricingExpression SPricingExpression
  33. currencyConversionRate int
  34. EffectiveTime time.Time
  35. }
  36. type SPricingExpression struct {
  37. UsageUnit string
  38. UsageUnitDescription string
  39. BaseUnit string
  40. BaseUnitDescription string
  41. BaseUnitConversionFactor string
  42. DisplayQuantity int
  43. TieredRates []STieredRate
  44. }
  45. type STieredRate struct {
  46. StartUsageAmount int
  47. UnitPrice SUnitPrice
  48. }
  49. type SUnitPrice struct {
  50. CurrencyCode string
  51. Units string
  52. Nanos int
  53. }
  54. type SSkuBilling struct {
  55. Name string
  56. SkuId string
  57. Description string
  58. Category SkuBillingCatetory
  59. ServiceRegions []string
  60. PricingInfo []SkuPricingInfo
  61. ServiceProviderName string
  62. }
  63. func (region *SRegion) ListSkuBilling(pageSize int, pageToken string) ([]SSkuBilling, error) {
  64. skus := []SSkuBilling{}
  65. params := map[string]string{}
  66. err := region.BillingList("services/6F81-5844-456A/skus", params, pageSize, pageToken, &skus)
  67. if err != nil {
  68. return nil, err
  69. }
  70. return skus, nil
  71. }
  72. type SRateInfo struct {
  73. // region: europe-north1
  74. // family: Compute, Storage, Network
  75. // resource: CPU, Ram, Gpu, N1Standard
  76. // category: custome, predefine, optimized
  77. // startUsageAmount:
  78. // map[region]map[family]map[resource]map[category]map[startUsageAmount][money]
  79. Info map[string]map[string]map[string]map[string]map[string]float64
  80. }
  81. func (region *SRegion) GetSkuRateInfo(skus []SSkuBilling) SRateInfo {
  82. result := SRateInfo{
  83. Info: map[string]map[string]map[string]map[string]map[string]float64{},
  84. }
  85. for _, sku := range skus {
  86. if sku.ServiceProviderName == "Google" &&
  87. sku.Category.ServiceDisplayName == "Compute Engine" &&
  88. utils.IsInStringArray(sku.Category.ResourceFamily, []string{"Compute", "Storage"}) &&
  89. sku.Category.UsageType == "OnDemand" {
  90. for _, region := range sku.ServiceRegions {
  91. if _, ok := result.Info[region]; !ok {
  92. result.Info[region] = map[string]map[string]map[string]map[string]float64{}
  93. }
  94. if _, ok := result.Info[region][sku.Category.ResourceFamily]; !ok {
  95. result.Info[region][sku.Category.ResourceFamily] = map[string]map[string]map[string]float64{}
  96. }
  97. if sku.Category.ResourceGroup == "N1Standard" {
  98. if strings.Index(sku.Description, "Core") > 0 {
  99. sku.Category.ResourceGroup = "CPU"
  100. } else if strings.Index(sku.Description, "Ram") > 0 {
  101. sku.Category.ResourceGroup = "RAM"
  102. }
  103. }
  104. if !utils.IsInStringArray(sku.Category.ResourceGroup, []string{"F1Micro", "G1Small", "CPU", "RAM", "PDStandard", "SSD", "LocalSSD", "f1-micro", "g1-small"}) {
  105. continue
  106. }
  107. if utils.IsInStringArray(sku.Category.ResourceGroup, []string{"PDStandard", "SSD"}) && strings.Index(sku.Description, "Regional") >= 0 {
  108. continue
  109. }
  110. convers := map[string]string{
  111. "PDStandard": api.STORAGE_GOOGLE_PD_STANDARD,
  112. "SSD": api.STORAGE_GOOGLE_PD_SSD,
  113. "LocalSSD": api.STORAGE_GOOGLE_LOCAL_SSD,
  114. "F1Micro": "f1-micro",
  115. "G1Small": "g1-small",
  116. }
  117. if group, ok := convers[sku.Category.ResourceGroup]; ok {
  118. sku.Category.ResourceGroup = group
  119. }
  120. if _, ok := result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup]; !ok {
  121. result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup] = map[string]map[string]float64{}
  122. }
  123. description := strings.ToLower(sku.Description)
  124. if strings.Contains("sole", description) { //单租户
  125. continue
  126. }
  127. category := ""
  128. keys := []string{"memory optimized", "memory-optimized", "compute optimized", "n1 predefined", "n2 instance", "n2 custom extended", "custom extended", "n2 custom", "custom instance"}
  129. categories := map[string]string{
  130. "memory optimized": "ultramem",
  131. "memory-optimized": "memory-optimized",
  132. "compute optimized": "compute-optimized",
  133. "n1 predefined": "n1-predefined",
  134. "n2 instance": "n2-instance",
  135. "n2 custom extended": "n2-custom-extended",
  136. "custom extended": "custom-extended",
  137. "n2 custom": "n2-custom", //cpu ram
  138. "custom instance": "custom-instance", //cpu
  139. }
  140. for _, key := range keys {
  141. _category := categories[key]
  142. if strings.Contains(description, key) {
  143. category = _category
  144. break
  145. }
  146. }
  147. if utils.IsInStringArray(sku.Category.ResourceGroup, []string{api.STORAGE_GOOGLE_PD_STANDARD, api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_LOCAL_SSD, "f1-micro", "g1-small"}) {
  148. category = sku.Category.ResourceGroup
  149. }
  150. if len(category) == 0 {
  151. continue
  152. }
  153. if _, ok := result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup][category]; !ok {
  154. result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup][category] = map[string]float64{}
  155. }
  156. for _, priceInfo := range sku.PricingInfo {
  157. for _, price := range priceInfo.PricingExpression.TieredRates {
  158. result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup][category][fmt.Sprintf("%d", price.StartUsageAmount)] = float64(price.UnitPrice.Nanos) / 1000000000
  159. }
  160. }
  161. }
  162. }
  163. }
  164. return result
  165. }
  166. func (rate *SRateInfo) GetDiscount(sku string) []float64 {
  167. if strings.Index(sku, "custom") < 0 && strings.HasPrefix(sku, "c") || strings.Index(sku, "n2") >= 0 {
  168. return []float64{0, 0.15, 0.25, 0.4}
  169. }
  170. return []float64{0, 0.2, 0.4, 0.6}
  171. }
  172. func (rate *SRateInfo) GetSkuType(sku string) (string, error) {
  173. if strings.Index(sku, "custom") >= 0 {
  174. cpuType := "custom-instance"
  175. if strings.HasPrefix(sku, "n2") {
  176. cpuType = "n2-custom"
  177. }
  178. return cpuType, nil
  179. }
  180. if strings.HasPrefix(sku, "n1") {
  181. return "n1-predefined", nil
  182. }
  183. if strings.HasPrefix(sku, "n2") {
  184. return "n2-instance", nil
  185. }
  186. if strings.HasPrefix(sku, "c") {
  187. return "compute-optimized", nil
  188. }
  189. if strings.HasPrefix(sku, "m") {
  190. return "memory-optimized", nil
  191. }
  192. return "", fmt.Errorf("failed to found sku %s type", sku)
  193. }
  194. func (rate *SRateInfo) GetCpuPrice(regionId, cpuType string) (float64, error) {
  195. computePrice, err := rate.GetComputePrice(regionId)
  196. if err != nil {
  197. return 0, errors.Wrap(err, "GetComputePrice")
  198. }
  199. _cpuPrice, ok := computePrice["CPU"]
  200. if !ok {
  201. return 0, fmt.Errorf("failed to found region %s compute cpu price info", regionId)
  202. }
  203. cpuPrice, ok := _cpuPrice[cpuType]
  204. if !ok {
  205. return 0, fmt.Errorf("failed to found region %s compute %s cpu price info", regionId, cpuType)
  206. }
  207. return cpuPrice["0"], nil
  208. }
  209. func (rate *SRateInfo) GetExtendMemoryGb(sku string, cpu int, memoryMb int) float64 {
  210. maxMemoryGb := 0.0
  211. if strings.Index(sku, "custom") >= 0 {
  212. maxMemoryGb = float64(cpu) * 6.5
  213. if strings.Index(sku, "n2") >= 0 {
  214. maxMemoryGb = float64(cpu) * 8
  215. }
  216. }
  217. if float64(memoryMb)/1024 > maxMemoryGb {
  218. return float64(memoryMb)/1024 - maxMemoryGb
  219. }
  220. return 0.0
  221. }
  222. func (rate *SRateInfo) GetMemoryPrice(regionId string, memoryType string) (float64, error) {
  223. computePrice, err := rate.GetComputePrice(regionId)
  224. if err != nil {
  225. return 0, errors.Wrap(err, "GetComputePrice")
  226. }
  227. _memoryPrice, ok := computePrice["RAM"]
  228. if !ok {
  229. return 0, fmt.Errorf("failed to found region %s compute memory price info", regionId)
  230. }
  231. memoryPrice, ok := _memoryPrice[memoryType]
  232. if !ok {
  233. return 0, fmt.Errorf("failed to found region %s compute %s memory price info", regionId, memoryType)
  234. }
  235. return memoryPrice["0"], nil
  236. }
  237. func (rate *SRateInfo) GetComputePrice(regionId string) (map[string]map[string]map[string]float64, error) {
  238. regionPrice, ok := rate.Info[regionId]
  239. if !ok {
  240. return nil, fmt.Errorf("failed to found region %s price info", regionId)
  241. }
  242. computePrice, ok := regionPrice["Compute"]
  243. if !ok {
  244. return nil, fmt.Errorf("failed to found region %s compute price info", regionId)
  245. }
  246. return computePrice, nil
  247. }
  248. func (rate *SRateInfo) GetSharedSkuPrice(regionId string, sku string) (float64, error) {
  249. computePrice, err := rate.GetComputePrice(regionId)
  250. if err != nil {
  251. return 0.0, errors.Wrap(err, "GetComputePrice")
  252. }
  253. if _sharedSku, ok := computePrice[sku]; ok {
  254. if sharedSku, ok := _sharedSku[sku]; ok {
  255. return sharedSku["0"], nil
  256. }
  257. }
  258. return 0.0, fmt.Errorf("sku is not shared sku")
  259. }
  260. func (rate *SRateInfo) GetSkuPrice(regionId string, sku string, cpu, memoryMb int) (struct {
  261. Hour float64
  262. Month float64
  263. Year float64
  264. }, error) {
  265. result := struct {
  266. Hour float64
  267. Month float64
  268. Year float64
  269. }{}
  270. discount := rate.GetDiscount(sku)
  271. price, err := rate.GetSharedSkuPrice(regionId, sku)
  272. if err != nil {
  273. skuType, err := rate.GetSkuType(sku)
  274. if err != nil {
  275. return result, errors.Wrap(err, "GetSkuType")
  276. }
  277. cpuPrice, err := rate.GetCpuPrice(regionId, skuType)
  278. if err != nil {
  279. return result, errors.Wrap(err, "price.GetCpuPrice")
  280. }
  281. price += float64(cpu) * cpuPrice
  282. log.Debugf("cpu price: %f", cpuPrice)
  283. extendMemoryGb := rate.GetExtendMemoryGb(sku, cpu, memoryMb)
  284. memoryMb = int(float64(memoryMb) - 1024*extendMemoryGb)
  285. memoryPrice, err := rate.GetMemoryPrice(regionId, skuType)
  286. if err != nil {
  287. return result, errors.Wrap(err, "GetMemoryPrice")
  288. }
  289. price += memoryPrice * float64(memoryMb/1024)
  290. log.Debugf("ramPrice: %f", memoryPrice)
  291. if extendMemoryGb > 0 {
  292. memoryType := "custom-extended"
  293. if strings.HasPrefix(sku, "n2") {
  294. memoryType = "n2-custom-extended"
  295. }
  296. extendPrice, err := rate.GetMemoryPrice(regionId, memoryType)
  297. if err != nil {
  298. return result, errors.Wrap(err, "GetMemoryPrice.Extend")
  299. }
  300. price += extendPrice * float64(extendMemoryGb)
  301. log.Debugf("extendPrice: %f", extendPrice)
  302. }
  303. }
  304. log.Debugf("totalPrice: %f", price)
  305. result.Month = 182.5 * price
  306. result.Month += 182.5 * (1 - discount[1]) * price
  307. result.Month += 182.5 * (1 - discount[2]) * price
  308. result.Month += 172.49999999999997 * (1 - discount[3]) * price
  309. result.Hour = result.Month / 30 / 24
  310. result.Year = result.Month * 12
  311. return result, nil
  312. }