| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package google
- import (
- "fmt"
- "strings"
- "time"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/utils"
- api "yunion.io/x/cloudmux/pkg/apis/compute"
- )
- type SkuBillingCatetory struct {
- ServiceDisplayName string
- ResourceFamily string
- ResourceGroup string
- UsageType string
- }
- type SkuPricingInfo struct {
- Summary string
- PricingExpression SPricingExpression
- currencyConversionRate int
- EffectiveTime time.Time
- }
- type SPricingExpression struct {
- UsageUnit string
- UsageUnitDescription string
- BaseUnit string
- BaseUnitDescription string
- BaseUnitConversionFactor string
- DisplayQuantity int
- TieredRates []STieredRate
- }
- type STieredRate struct {
- StartUsageAmount int
- UnitPrice SUnitPrice
- }
- type SUnitPrice struct {
- CurrencyCode string
- Units string
- Nanos int
- }
- type SSkuBilling struct {
- Name string
- SkuId string
- Description string
- Category SkuBillingCatetory
- ServiceRegions []string
- PricingInfo []SkuPricingInfo
- ServiceProviderName string
- }
- func (region *SRegion) ListSkuBilling(pageSize int, pageToken string) ([]SSkuBilling, error) {
- skus := []SSkuBilling{}
- params := map[string]string{}
- err := region.BillingList("services/6F81-5844-456A/skus", params, pageSize, pageToken, &skus)
- if err != nil {
- return nil, err
- }
- return skus, nil
- }
- type SRateInfo struct {
- // region: europe-north1
- // family: Compute, Storage, Network
- // resource: CPU, Ram, Gpu, N1Standard
- // category: custome, predefine, optimized
- // startUsageAmount:
- // map[region]map[family]map[resource]map[category]map[startUsageAmount][money]
- Info map[string]map[string]map[string]map[string]map[string]float64
- }
- func (region *SRegion) GetSkuRateInfo(skus []SSkuBilling) SRateInfo {
- result := SRateInfo{
- Info: map[string]map[string]map[string]map[string]map[string]float64{},
- }
- for _, sku := range skus {
- if sku.ServiceProviderName == "Google" &&
- sku.Category.ServiceDisplayName == "Compute Engine" &&
- utils.IsInStringArray(sku.Category.ResourceFamily, []string{"Compute", "Storage"}) &&
- sku.Category.UsageType == "OnDemand" {
- for _, region := range sku.ServiceRegions {
- if _, ok := result.Info[region]; !ok {
- result.Info[region] = map[string]map[string]map[string]map[string]float64{}
- }
- if _, ok := result.Info[region][sku.Category.ResourceFamily]; !ok {
- result.Info[region][sku.Category.ResourceFamily] = map[string]map[string]map[string]float64{}
- }
- if sku.Category.ResourceGroup == "N1Standard" {
- if strings.Index(sku.Description, "Core") > 0 {
- sku.Category.ResourceGroup = "CPU"
- } else if strings.Index(sku.Description, "Ram") > 0 {
- sku.Category.ResourceGroup = "RAM"
- }
- }
- if !utils.IsInStringArray(sku.Category.ResourceGroup, []string{"F1Micro", "G1Small", "CPU", "RAM", "PDStandard", "SSD", "LocalSSD", "f1-micro", "g1-small"}) {
- continue
- }
- if utils.IsInStringArray(sku.Category.ResourceGroup, []string{"PDStandard", "SSD"}) && strings.Index(sku.Description, "Regional") >= 0 {
- continue
- }
- convers := map[string]string{
- "PDStandard": api.STORAGE_GOOGLE_PD_STANDARD,
- "SSD": api.STORAGE_GOOGLE_PD_SSD,
- "LocalSSD": api.STORAGE_GOOGLE_LOCAL_SSD,
- "F1Micro": "f1-micro",
- "G1Small": "g1-small",
- }
- if group, ok := convers[sku.Category.ResourceGroup]; ok {
- sku.Category.ResourceGroup = group
- }
- if _, ok := result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup]; !ok {
- result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup] = map[string]map[string]float64{}
- }
- description := strings.ToLower(sku.Description)
- if strings.Contains("sole", description) { //单租户
- continue
- }
- category := ""
- keys := []string{"memory optimized", "memory-optimized", "compute optimized", "n1 predefined", "n2 instance", "n2 custom extended", "custom extended", "n2 custom", "custom instance"}
- categories := map[string]string{
- "memory optimized": "ultramem",
- "memory-optimized": "memory-optimized",
- "compute optimized": "compute-optimized",
- "n1 predefined": "n1-predefined",
- "n2 instance": "n2-instance",
- "n2 custom extended": "n2-custom-extended",
- "custom extended": "custom-extended",
- "n2 custom": "n2-custom", //cpu ram
- "custom instance": "custom-instance", //cpu
- }
- for _, key := range keys {
- _category := categories[key]
- if strings.Contains(description, key) {
- category = _category
- break
- }
- }
- 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"}) {
- category = sku.Category.ResourceGroup
- }
- if len(category) == 0 {
- continue
- }
- if _, ok := result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup][category]; !ok {
- result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup][category] = map[string]float64{}
- }
- for _, priceInfo := range sku.PricingInfo {
- for _, price := range priceInfo.PricingExpression.TieredRates {
- result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup][category][fmt.Sprintf("%d", price.StartUsageAmount)] = float64(price.UnitPrice.Nanos) / 1000000000
- }
- }
- }
- }
- }
- return result
- }
- func (rate *SRateInfo) GetDiscount(sku string) []float64 {
- if strings.Index(sku, "custom") < 0 && strings.HasPrefix(sku, "c") || strings.Index(sku, "n2") >= 0 {
- return []float64{0, 0.15, 0.25, 0.4}
- }
- return []float64{0, 0.2, 0.4, 0.6}
- }
- func (rate *SRateInfo) GetSkuType(sku string) (string, error) {
- if strings.Index(sku, "custom") >= 0 {
- cpuType := "custom-instance"
- if strings.HasPrefix(sku, "n2") {
- cpuType = "n2-custom"
- }
- return cpuType, nil
- }
- if strings.HasPrefix(sku, "n1") {
- return "n1-predefined", nil
- }
- if strings.HasPrefix(sku, "n2") {
- return "n2-instance", nil
- }
- if strings.HasPrefix(sku, "c") {
- return "compute-optimized", nil
- }
- if strings.HasPrefix(sku, "m") {
- return "memory-optimized", nil
- }
- return "", fmt.Errorf("failed to found sku %s type", sku)
- }
- func (rate *SRateInfo) GetCpuPrice(regionId, cpuType string) (float64, error) {
- computePrice, err := rate.GetComputePrice(regionId)
- if err != nil {
- return 0, errors.Wrap(err, "GetComputePrice")
- }
- _cpuPrice, ok := computePrice["CPU"]
- if !ok {
- return 0, fmt.Errorf("failed to found region %s compute cpu price info", regionId)
- }
- cpuPrice, ok := _cpuPrice[cpuType]
- if !ok {
- return 0, fmt.Errorf("failed to found region %s compute %s cpu price info", regionId, cpuType)
- }
- return cpuPrice["0"], nil
- }
- func (rate *SRateInfo) GetExtendMemoryGb(sku string, cpu int, memoryMb int) float64 {
- maxMemoryGb := 0.0
- if strings.Index(sku, "custom") >= 0 {
- maxMemoryGb = float64(cpu) * 6.5
- if strings.Index(sku, "n2") >= 0 {
- maxMemoryGb = float64(cpu) * 8
- }
- }
- if float64(memoryMb)/1024 > maxMemoryGb {
- return float64(memoryMb)/1024 - maxMemoryGb
- }
- return 0.0
- }
- func (rate *SRateInfo) GetMemoryPrice(regionId string, memoryType string) (float64, error) {
- computePrice, err := rate.GetComputePrice(regionId)
- if err != nil {
- return 0, errors.Wrap(err, "GetComputePrice")
- }
- _memoryPrice, ok := computePrice["RAM"]
- if !ok {
- return 0, fmt.Errorf("failed to found region %s compute memory price info", regionId)
- }
- memoryPrice, ok := _memoryPrice[memoryType]
- if !ok {
- return 0, fmt.Errorf("failed to found region %s compute %s memory price info", regionId, memoryType)
- }
- return memoryPrice["0"], nil
- }
- func (rate *SRateInfo) GetComputePrice(regionId string) (map[string]map[string]map[string]float64, error) {
- regionPrice, ok := rate.Info[regionId]
- if !ok {
- return nil, fmt.Errorf("failed to found region %s price info", regionId)
- }
- computePrice, ok := regionPrice["Compute"]
- if !ok {
- return nil, fmt.Errorf("failed to found region %s compute price info", regionId)
- }
- return computePrice, nil
- }
- func (rate *SRateInfo) GetSharedSkuPrice(regionId string, sku string) (float64, error) {
- computePrice, err := rate.GetComputePrice(regionId)
- if err != nil {
- return 0.0, errors.Wrap(err, "GetComputePrice")
- }
- if _sharedSku, ok := computePrice[sku]; ok {
- if sharedSku, ok := _sharedSku[sku]; ok {
- return sharedSku["0"], nil
- }
- }
- return 0.0, fmt.Errorf("sku is not shared sku")
- }
- func (rate *SRateInfo) GetSkuPrice(regionId string, sku string, cpu, memoryMb int) (struct {
- Hour float64
- Month float64
- Year float64
- }, error) {
- result := struct {
- Hour float64
- Month float64
- Year float64
- }{}
- discount := rate.GetDiscount(sku)
- price, err := rate.GetSharedSkuPrice(regionId, sku)
- if err != nil {
- skuType, err := rate.GetSkuType(sku)
- if err != nil {
- return result, errors.Wrap(err, "GetSkuType")
- }
- cpuPrice, err := rate.GetCpuPrice(regionId, skuType)
- if err != nil {
- return result, errors.Wrap(err, "price.GetCpuPrice")
- }
- price += float64(cpu) * cpuPrice
- log.Debugf("cpu price: %f", cpuPrice)
- extendMemoryGb := rate.GetExtendMemoryGb(sku, cpu, memoryMb)
- memoryMb = int(float64(memoryMb) - 1024*extendMemoryGb)
- memoryPrice, err := rate.GetMemoryPrice(regionId, skuType)
- if err != nil {
- return result, errors.Wrap(err, "GetMemoryPrice")
- }
- price += memoryPrice * float64(memoryMb/1024)
- log.Debugf("ramPrice: %f", memoryPrice)
- if extendMemoryGb > 0 {
- memoryType := "custom-extended"
- if strings.HasPrefix(sku, "n2") {
- memoryType = "n2-custom-extended"
- }
- extendPrice, err := rate.GetMemoryPrice(regionId, memoryType)
- if err != nil {
- return result, errors.Wrap(err, "GetMemoryPrice.Extend")
- }
- price += extendPrice * float64(extendMemoryGb)
- log.Debugf("extendPrice: %f", extendPrice)
- }
- }
- log.Debugf("totalPrice: %f", price)
- result.Month = 182.5 * price
- result.Month += 182.5 * (1 - discount[1]) * price
- result.Month += 182.5 * (1 - discount[2]) * price
- result.Month += 172.49999999999997 * (1 - discount[3]) * price
- result.Hour = result.Month / 30 / 24
- result.Year = result.Month * 12
- return result, nil
- }
|