provider.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. package oracle
  2. import (
  3. "fmt"
  4. "io"
  5. "strconv"
  6. "sync"
  7. "time"
  8. "github.com/opencost/opencost/core/pkg/log"
  9. "github.com/opencost/opencost/core/pkg/opencost"
  10. "github.com/opencost/opencost/core/pkg/util"
  11. "github.com/opencost/opencost/core/pkg/util/json"
  12. "github.com/opencost/opencost/pkg/cloud/models"
  13. "github.com/opencost/opencost/pkg/cloud/utils"
  14. "github.com/opencost/opencost/pkg/clustercache"
  15. "github.com/opencost/opencost/pkg/env"
  16. v1 "k8s.io/api/core/v1"
  17. )
  18. const nodePoolIdAnnotation = "oci.oraclecloud.com/node-pool-id"
  19. const virtualPoolIdAnnotation = "oci.oraclecloud.com/virtual-node-pool-id"
  20. const virtualNodeLabel = "node-role.kubernetes.io/virtual-node"
  21. const managementPlatformOKE = "oke"
  22. const currencyCodeUSD = "USD"
  23. type Oracle struct {
  24. Config models.ProviderConfig
  25. Clientset clustercache.ClusterCache
  26. ClusterRegion string
  27. ClusterAccountID string
  28. DownloadPricingDataLock sync.RWMutex
  29. OSEnvLock sync.Mutex
  30. RateCardStore *RateCardStore
  31. ServiceAccountChecks *models.ServiceAccountChecks
  32. DefaultPricing DefaultPricing
  33. }
  34. func (o *Oracle) ClusterInfo() (map[string]string, error) {
  35. c, err := o.GetConfig()
  36. if err != nil {
  37. return nil, err
  38. }
  39. m := make(map[string]string)
  40. m["name"] = "Oracle Cluster #1"
  41. if clusterName := o.getClusterName(c); clusterName != "" {
  42. m["name"] = clusterName
  43. }
  44. m["provider"] = opencost.OracleProvider
  45. m["account"] = o.ClusterAccountID
  46. m["region"] = o.ClusterRegion
  47. m["remoteReadEnabled"] = strconv.FormatBool(env.IsRemoteEnabled())
  48. m["id"] = env.GetClusterID()
  49. return m, nil
  50. }
  51. func (o *Oracle) NodePricing(key models.Key) (*models.Node, models.PricingMetadata, error) {
  52. if err := o.ensurePricingData(); err != nil {
  53. return nil, models.PricingMetadata{}, err
  54. }
  55. o.DownloadPricingDataLock.RLock()
  56. defer o.DownloadPricingDataLock.RUnlock()
  57. return o.RateCardStore.ForKey(key, o.DefaultPricing)
  58. }
  59. func (o *Oracle) PVPricing(pvk models.PVKey) (*models.PV, error) {
  60. if err := o.ensurePricingData(); err != nil {
  61. return nil, err
  62. }
  63. o.DownloadPricingDataLock.RLock()
  64. defer o.DownloadPricingDataLock.RUnlock()
  65. return o.RateCardStore.ForPVK(pvk, o.DefaultPricing)
  66. }
  67. func (o *Oracle) NetworkPricing() (*models.Network, error) {
  68. if err := o.ensurePricingData(); err != nil {
  69. return nil, err
  70. }
  71. o.DownloadPricingDataLock.RLock()
  72. defer o.DownloadPricingDataLock.RUnlock()
  73. return o.RateCardStore.ForEgressRegion(o.ClusterRegion, o.DefaultPricing)
  74. }
  75. func (o *Oracle) LoadBalancerPricing() (*models.LoadBalancer, error) {
  76. if err := o.ensurePricingData(); err != nil {
  77. return nil, err
  78. }
  79. o.DownloadPricingDataLock.RLock()
  80. defer o.DownloadPricingDataLock.RUnlock()
  81. return o.RateCardStore.ForLB(o.DefaultPricing)
  82. }
  83. func (o *Oracle) AllNodePricing() (interface{}, error) {
  84. if err := o.ensurePricingData(); err != nil {
  85. return nil, err
  86. }
  87. o.DownloadPricingDataLock.RLock()
  88. defer o.DownloadPricingDataLock.RUnlock()
  89. return o.RateCardStore.Store(), nil
  90. }
  91. // DownloadPricingData refreshes the RateCardStore pricing data.
  92. func (o *Oracle) DownloadPricingData() error {
  93. o.DownloadPricingDataLock.Lock()
  94. defer o.DownloadPricingDataLock.Unlock()
  95. cfg, err := o.GetConfig()
  96. if err != nil {
  97. return err
  98. }
  99. if o.RateCardStore == nil {
  100. url := env.GetOCIPricingURL()
  101. o.RateCardStore = NewRateCardStore(url, cfg.CurrencyCode)
  102. }
  103. if _, err := o.RateCardStore.Refresh(); err != nil {
  104. return err
  105. }
  106. o.DefaultPricing = DefaultPricing{
  107. OCPU: cfg.CPU,
  108. Memory: cfg.RAM,
  109. GPU: cfg.GPU,
  110. Storage: cfg.Storage,
  111. Egress: cfg.InternetNetworkEgress,
  112. LB: cfg.DefaultLBPrice,
  113. }
  114. return nil
  115. }
  116. func (o *Oracle) GetKey(labels map[string]string, n *v1.Node) models.Key {
  117. var gpuCount int
  118. var gpuType string
  119. if gpuc, ok := n.Status.Capacity["nvidia.com/gpu"]; ok {
  120. gpuCount = int(gpuc.Value())
  121. gpuType = "nvidia.com/gpu"
  122. }
  123. instanceType, _ := util.GetInstanceType(labels)
  124. return &oracleKey{
  125. providerID: n.Spec.ProviderID,
  126. instanceType: instanceType,
  127. labels: labels,
  128. gpuCount: gpuCount,
  129. gpuType: gpuType,
  130. }
  131. }
  132. func (o *Oracle) GetPVKey(pv *v1.PersistentVolume, parameters map[string]string, _ string) models.PVKey {
  133. var providerID string
  134. var driver string
  135. if pv.Spec.CSI != nil {
  136. providerID = pv.Spec.CSI.VolumeHandle
  137. driver = pv.Spec.CSI.Driver
  138. }
  139. return &oraclePVKey{
  140. storageClass: pv.Spec.StorageClassName,
  141. providerID: providerID,
  142. driver: driver,
  143. parameters: parameters,
  144. }
  145. }
  146. func (o *Oracle) UpdateConfig(r io.Reader, _ string) (*models.CustomPricing, error) {
  147. return o.Config.Update(func(pricing *models.CustomPricing) error {
  148. a := make(map[string]interface{})
  149. err := json.NewDecoder(r).Decode(&a)
  150. if err != nil {
  151. return err
  152. }
  153. for k, v := range a {
  154. kUpper := utils.ToTitle.String(k)
  155. vstr, ok := v.(string)
  156. if ok {
  157. err := models.SetCustomPricingField(pricing, kUpper, vstr)
  158. if err != nil {
  159. return fmt.Errorf("error setting custom pricing field: %w", err)
  160. }
  161. } else {
  162. return fmt.Errorf("type error while updating config for %s", kUpper)
  163. }
  164. }
  165. if env.IsRemoteEnabled() {
  166. err := utils.UpdateClusterMeta(env.GetClusterID(), o.getClusterName(pricing))
  167. if err != nil {
  168. return err
  169. }
  170. }
  171. return nil
  172. })
  173. }
  174. func (o *Oracle) UpdateConfigFromConfigMap(m map[string]string) (*models.CustomPricing, error) {
  175. return o.Config.UpdateFromMap(m)
  176. }
  177. func (o *Oracle) GetConfig() (*models.CustomPricing, error) {
  178. c, err := o.Config.GetCustomPricingData()
  179. if err != nil {
  180. return nil, err
  181. }
  182. if c.Discount == "" {
  183. c.Discount = "0%"
  184. }
  185. if c.NegotiatedDiscount == "" {
  186. c.NegotiatedDiscount = "0%"
  187. }
  188. if c.CurrencyCode == "" {
  189. c.CurrencyCode = currencyCodeUSD
  190. }
  191. return c, nil
  192. }
  193. func (o *Oracle) GetManagementPlatform() (string, error) {
  194. nodes := o.Clientset.GetAllNodes()
  195. for _, node := range nodes {
  196. if _, ok := node.GetObjectMeta().GetAnnotations()[nodePoolIdAnnotation]; ok {
  197. return managementPlatformOKE, nil
  198. }
  199. if _, ok := node.GetObjectMeta().GetAnnotations()[virtualPoolIdAnnotation]; ok {
  200. return managementPlatformOKE, nil
  201. }
  202. }
  203. return "", nil
  204. }
  205. func (o *Oracle) PricingSourceStatus() map[string]*models.PricingSource {
  206. listPricing := "List Pricing"
  207. return map[string]*models.PricingSource{
  208. listPricing: {
  209. Name: listPricing,
  210. Enabled: true,
  211. Available: true,
  212. },
  213. }
  214. }
  215. func (o *Oracle) ClusterManagementPricing() (string, float64, error) {
  216. if err := o.ensurePricingData(); err != nil {
  217. return "", 0.0, err
  218. }
  219. managementPlatform, _ := o.GetManagementPlatform()
  220. if managementPlatform != managementPlatformOKE {
  221. return "", 0.0, nil // Self-managed cluster.
  222. }
  223. o.DownloadPricingDataLock.Lock()
  224. defer o.DownloadPricingDataLock.Unlock()
  225. // TODO: Support lookup of cluster type, as BASIC_CLUSTER types are free.
  226. return managementPlatformOKE, o.RateCardStore.ForManagedCluster("ENHANCED_CLUSTER"), nil
  227. }
  228. func (o *Oracle) CombinedDiscountForNode(instanceType string, isPreemptible bool, defaultDiscount, negotiatedDiscount float64) float64 {
  229. return 1.0 - ((1.0 - defaultDiscount) * (1.0 - negotiatedDiscount))
  230. }
  231. func (o *Oracle) Regions() []string {
  232. regionOverrides := env.GetRegionOverrideList()
  233. if len(regionOverrides) > 0 {
  234. log.Debugf("Overriding Oracle regions with configured region list: %+v", regionOverrides)
  235. return regionOverrides
  236. }
  237. return oracleRegions
  238. }
  239. func (o *Oracle) PricingSourceSummary() interface{} {
  240. if err := o.ensurePricingData(); err != nil {
  241. return err
  242. }
  243. o.DownloadPricingDataLock.Lock()
  244. defer o.DownloadPricingDataLock.Unlock()
  245. return o.RateCardStore.Store()
  246. }
  247. func (o *Oracle) getClusterName(cfg *models.CustomPricing) string {
  248. if cfg.ClusterName != "" {
  249. return cfg.ClusterName
  250. }
  251. for _, node := range o.Clientset.GetAllNodes() {
  252. if clusterName, ok := node.Labels["name"]; ok {
  253. return clusterName
  254. }
  255. }
  256. return ""
  257. }
  258. func (o *Oracle) ensurePricingData() error {
  259. if o.RateCardStore == nil {
  260. return o.DownloadPricingData()
  261. }
  262. return nil
  263. }
  264. func (o *Oracle) GetAddresses() ([]byte, error) {
  265. return nil, nil
  266. }
  267. func (o *Oracle) GetDisks() ([]byte, error) {
  268. return nil, nil
  269. }
  270. func (o *Oracle) GetOrphanedResources() ([]models.OrphanedResource, error) {
  271. return nil, nil
  272. }
  273. func (o *Oracle) GetLocalStorageQuery(duration time.Duration, duration2 time.Duration, b bool, b2 bool) string {
  274. return ""
  275. }
  276. func (o *Oracle) ApplyReservedInstancePricing(m map[string]*models.Node) {}
  277. func (o *Oracle) ServiceAccountStatus() *models.ServiceAccountStatus {
  278. return o.ServiceAccountChecks.GetStatus()
  279. }