provider.go 8.5 KB

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