2
0

resultparsers.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. package costmodel
  2. import (
  3. "errors"
  4. "fmt"
  5. "time"
  6. "github.com/opencost/opencost/core/pkg/clustercache"
  7. "github.com/opencost/opencost/core/pkg/log"
  8. "github.com/opencost/opencost/core/pkg/source"
  9. "github.com/opencost/opencost/core/pkg/util"
  10. costAnalyzerCloud "github.com/opencost/opencost/pkg/cloud/models"
  11. )
  12. func GetPVInfoLocal(cache clustercache.ClusterCache, defaultClusterID string) (map[string]*PersistentVolumeClaimData, error) {
  13. toReturn := make(map[string]*PersistentVolumeClaimData)
  14. pvcs := cache.GetAllPersistentVolumeClaims()
  15. for _, pvc := range pvcs {
  16. var vals []*util.Vector
  17. vals = append(vals, &util.Vector{
  18. Timestamp: float64(time.Now().Unix()),
  19. Value: float64(pvc.Spec.Resources.Requests.Storage().Value()),
  20. })
  21. ns := pvc.Namespace
  22. pvcName := pvc.Name
  23. volumeName := pvc.Spec.VolumeName
  24. pvClass := ""
  25. if pvc.Spec.StorageClassName != nil {
  26. pvClass = *pvc.Spec.StorageClassName
  27. }
  28. clusterID := defaultClusterID
  29. key := fmt.Sprintf("%s,%s,%s", ns, pvcName, clusterID)
  30. toReturn[key] = &PersistentVolumeClaimData{
  31. Class: pvClass,
  32. Claim: pvcName,
  33. Namespace: ns,
  34. ClusterID: clusterID,
  35. VolumeName: volumeName,
  36. Values: vals,
  37. }
  38. }
  39. return toReturn, nil
  40. }
  41. // TODO niko/prom move parsing functions from costmodel.go
  42. func GetPVInfo(qrs []*source.QueryResult, defaultClusterID string) (map[string]*PersistentVolumeClaimData, error) {
  43. toReturn := make(map[string]*PersistentVolumeClaimData)
  44. for _, val := range qrs {
  45. clusterID, _ := val.GetCluster()
  46. if clusterID == "" {
  47. clusterID = defaultClusterID
  48. }
  49. ns, err := val.GetNamespace()
  50. if err != nil {
  51. return toReturn, err
  52. }
  53. pvcName, err := val.GetString("persistentvolumeclaim")
  54. if err != nil {
  55. return toReturn, err
  56. }
  57. volumeName, err := val.GetString("volumename")
  58. if err != nil {
  59. log.Debugf("Unfulfilled claim %s: volumename field does not exist in data result vector", pvcName)
  60. volumeName = ""
  61. }
  62. pvClass, err := val.GetString("storageclass")
  63. if err != nil {
  64. // TODO: We need to look up the actual PV and PV capacity. For now just proceed with "".
  65. log.DedupedWarningf(5, "Storage Class not found for claim \"%s/%s\".", ns, pvcName)
  66. pvClass = ""
  67. }
  68. key := fmt.Sprintf("%s,%s,%s", ns, pvcName, clusterID)
  69. toReturn[key] = &PersistentVolumeClaimData{
  70. Class: pvClass,
  71. Claim: pvcName,
  72. Namespace: ns,
  73. ClusterID: clusterID,
  74. VolumeName: volumeName,
  75. Values: val.Values,
  76. }
  77. }
  78. return toReturn, nil
  79. }
  80. func GetPVAllocationMetrics(qrs []*source.QueryResult, defaultClusterID string) (map[string][]*PersistentVolumeClaimData, error) {
  81. toReturn := make(map[string][]*PersistentVolumeClaimData)
  82. for _, val := range qrs {
  83. clusterID, _ := val.GetCluster()
  84. if clusterID == "" {
  85. clusterID = defaultClusterID
  86. }
  87. ns, err := val.GetNamespace()
  88. if err != nil {
  89. return toReturn, err
  90. }
  91. pod, err := val.GetPod()
  92. if err != nil {
  93. return toReturn, err
  94. }
  95. pvcName, err := val.GetString("persistentvolumeclaim")
  96. if err != nil {
  97. return toReturn, err
  98. }
  99. pvName, err := val.GetString("persistentvolume")
  100. if err != nil {
  101. log.Warnf("persistentvolume field does not exist for pv %s", pvcName) // This is possible for an unfulfilled claim
  102. continue
  103. }
  104. key := fmt.Sprintf("%s,%s,%s", ns, pod, clusterID)
  105. pvcData := &PersistentVolumeClaimData{
  106. Class: "",
  107. Claim: pvcName,
  108. Namespace: ns,
  109. ClusterID: clusterID,
  110. VolumeName: pvName,
  111. Values: val.Values,
  112. }
  113. toReturn[key] = append(toReturn[key], pvcData)
  114. }
  115. return toReturn, nil
  116. }
  117. func GetPVCostMetrics(qrs []*source.QueryResult, defaultClusterID string) (map[string]*costAnalyzerCloud.PV, error) {
  118. toReturn := make(map[string]*costAnalyzerCloud.PV)
  119. for _, val := range qrs {
  120. clusterID, _ := val.GetCluster()
  121. if clusterID == "" {
  122. clusterID = defaultClusterID
  123. }
  124. volumeName, err := val.GetString("volumename")
  125. if err != nil {
  126. return toReturn, err
  127. }
  128. key := fmt.Sprintf("%s,%s", volumeName, clusterID)
  129. toReturn[key] = &costAnalyzerCloud.PV{
  130. Cost: fmt.Sprintf("%f", val.Values[0].Value),
  131. }
  132. }
  133. return toReturn, nil
  134. }
  135. func GetNamespaceLabelsMetrics(qrs []*source.QueryResult, defaultClusterID string) (map[string]map[string]string, error) {
  136. toReturn := make(map[string]map[string]string)
  137. for _, val := range qrs {
  138. // We want Namespace and ClusterID for key generation purposes
  139. ns, err := val.GetNamespace()
  140. if err != nil {
  141. return toReturn, err
  142. }
  143. clusterID, _ := val.GetCluster()
  144. if clusterID == "" {
  145. clusterID = defaultClusterID
  146. }
  147. nsKey := ns + "," + clusterID
  148. if nsLabels, ok := toReturn[nsKey]; ok {
  149. for k, v := range val.GetLabels() {
  150. nsLabels[k] = v // override with more recently assigned if we changed labels within the window.
  151. }
  152. } else {
  153. toReturn[nsKey] = val.GetLabels()
  154. }
  155. }
  156. return toReturn, nil
  157. }
  158. func GetPodLabelsMetrics(qrs []*source.QueryResult, defaultClusterID string) (map[string]map[string]string, error) {
  159. toReturn := make(map[string]map[string]string)
  160. for _, val := range qrs {
  161. // We want Pod, Namespace and ClusterID for key generation purposes
  162. pod, err := val.GetPod()
  163. if err != nil {
  164. return toReturn, err
  165. }
  166. ns, err := val.GetNamespace()
  167. if err != nil {
  168. return toReturn, err
  169. }
  170. clusterID, _ := val.GetCluster()
  171. if clusterID == "" {
  172. clusterID = defaultClusterID
  173. }
  174. nsKey := ns + "," + pod + "," + clusterID
  175. if labels, ok := toReturn[nsKey]; ok {
  176. newlabels := val.GetLabels()
  177. for k, v := range newlabels {
  178. labels[k] = v
  179. }
  180. } else {
  181. toReturn[nsKey] = val.GetLabels()
  182. }
  183. }
  184. return toReturn, nil
  185. }
  186. func GetNamespaceAnnotationsMetrics(qrs []*source.QueryResult, defaultClusterID string) (map[string]map[string]string, error) {
  187. toReturn := make(map[string]map[string]string)
  188. for _, val := range qrs {
  189. // We want Namespace and ClusterID for key generation purposes
  190. ns, err := val.GetNamespace()
  191. if err != nil {
  192. return toReturn, err
  193. }
  194. clusterID, _ := val.GetCluster()
  195. if clusterID == "" {
  196. clusterID = defaultClusterID
  197. }
  198. nsKey := ns + "," + clusterID
  199. if nsAnnotations, ok := toReturn[nsKey]; ok {
  200. for k, v := range val.GetAnnotations() {
  201. nsAnnotations[k] = v // override with more recently assigned if we changed labels within the window.
  202. }
  203. } else {
  204. toReturn[nsKey] = val.GetAnnotations()
  205. }
  206. }
  207. return toReturn, nil
  208. }
  209. func GetPodAnnotationsMetrics(qrs []*source.QueryResult, defaultClusterID string) (map[string]map[string]string, error) {
  210. toReturn := make(map[string]map[string]string)
  211. for _, val := range qrs {
  212. // We want Pod, Namespace and ClusterID for key generation purposes
  213. pod, err := val.GetPod()
  214. if err != nil {
  215. return toReturn, err
  216. }
  217. ns, err := val.GetNamespace()
  218. if err != nil {
  219. return toReturn, err
  220. }
  221. clusterID, _ := val.GetCluster()
  222. if clusterID == "" {
  223. clusterID = defaultClusterID
  224. }
  225. nsKey := ns + "," + pod + "," + clusterID
  226. if labels, ok := toReturn[nsKey]; ok {
  227. for k, v := range val.GetAnnotations() {
  228. labels[k] = v
  229. }
  230. } else {
  231. toReturn[nsKey] = val.GetAnnotations()
  232. }
  233. }
  234. return toReturn, nil
  235. }
  236. func GetStatefulsetMatchLabelsMetrics(qrs []*source.QueryResult, defaultClusterID string) (map[string]map[string]string, error) {
  237. toReturn := make(map[string]map[string]string)
  238. for _, val := range qrs {
  239. // We want Statefulset, Namespace and ClusterID for key generation purposes
  240. ss, err := val.GetString("statefulSet")
  241. if err != nil {
  242. return toReturn, err
  243. }
  244. ns, err := val.GetNamespace()
  245. if err != nil {
  246. return toReturn, err
  247. }
  248. clusterID, _ := val.GetCluster()
  249. if clusterID == "" {
  250. clusterID = defaultClusterID
  251. }
  252. nsKey := ns + "," + ss + "," + clusterID
  253. toReturn[nsKey] = val.GetLabels()
  254. }
  255. return toReturn, nil
  256. }
  257. func GetPodDaemonsetsWithMetrics(qrs []*source.QueryResult, defaultClusterID string) (map[string]string, error) {
  258. toReturn := make(map[string]string)
  259. for _, val := range qrs {
  260. ds, err := val.GetString("owner_name")
  261. if err != nil {
  262. return toReturn, err
  263. }
  264. ns, err := val.GetNamespace()
  265. if err != nil {
  266. return toReturn, err
  267. }
  268. clusterID, _ := val.GetCluster()
  269. if clusterID == "" {
  270. clusterID = defaultClusterID
  271. }
  272. pod, err := val.GetPod()
  273. if err != nil {
  274. return toReturn, err
  275. }
  276. nsKey := ns + "," + pod + "," + clusterID
  277. toReturn[nsKey] = ds
  278. }
  279. return toReturn, nil
  280. }
  281. func GetPodJobsWithMetrics(qrs []*source.QueryResult, defaultClusterID string) (map[string]string, error) {
  282. toReturn := make(map[string]string)
  283. for _, val := range qrs {
  284. ds, err := val.GetString("owner_name")
  285. if err != nil {
  286. return toReturn, err
  287. }
  288. ns, err := val.GetNamespace()
  289. if err != nil {
  290. return toReturn, err
  291. }
  292. clusterID, _ := val.GetCluster()
  293. if clusterID == "" {
  294. clusterID = defaultClusterID
  295. }
  296. pod, err := val.GetPod()
  297. if err != nil {
  298. return toReturn, err
  299. }
  300. nsKey := ns + "," + pod + "," + clusterID
  301. toReturn[nsKey] = ds
  302. }
  303. return toReturn, nil
  304. }
  305. func GetDeploymentMatchLabelsMetrics(qrs []*source.QueryResult, defaultClusterID string) (map[string]map[string]string, error) {
  306. toReturn := make(map[string]map[string]string)
  307. for _, val := range qrs {
  308. // We want Deployment, Namespace and ClusterID for key generation purposes
  309. deployment, err := val.GetString("deployment")
  310. if err != nil {
  311. return toReturn, err
  312. }
  313. ns, err := val.GetNamespace()
  314. if err != nil {
  315. return toReturn, err
  316. }
  317. clusterID, _ := val.GetCluster()
  318. if clusterID == "" {
  319. clusterID = defaultClusterID
  320. }
  321. nsKey := ns + "," + deployment + "," + clusterID
  322. toReturn[nsKey] = val.GetLabels()
  323. }
  324. return toReturn, nil
  325. }
  326. func GetServiceSelectorLabelsMetrics(qrs []*source.QueryResult, defaultClusterID string) (map[string]map[string]string, error) {
  327. toReturn := make(map[string]map[string]string)
  328. for _, val := range qrs {
  329. // We want Service, Namespace and ClusterID for key generation purposes
  330. service, err := val.GetString("service")
  331. if err != nil {
  332. return toReturn, err
  333. }
  334. ns, err := val.GetNamespace()
  335. if err != nil {
  336. return toReturn, err
  337. }
  338. clusterID, _ := val.GetCluster()
  339. if clusterID == "" {
  340. clusterID = defaultClusterID
  341. }
  342. nsKey := ns + "," + service + "," + clusterID
  343. toReturn[nsKey] = val.GetLabels()
  344. }
  345. return toReturn, nil
  346. }
  347. func GetContainerMetricVector(qrs []*source.ContainerMetricResult, defaultClusterID string) (map[string][]*util.Vector, error) {
  348. containerData := make(map[string][]*util.Vector)
  349. for _, val := range qrs {
  350. containerMetric, err := NewContainerMetricFrom(val, defaultClusterID)
  351. if err != nil {
  352. return nil, err
  353. }
  354. containerData[containerMetric.Key()] = val.Data
  355. }
  356. return containerData, nil
  357. }
  358. func GetContainerMetricVectors(qrs []*source.ContainerMetricResult, defaultClusterID string) (map[string][]*util.Vector, error) {
  359. containerData := make(map[string][]*util.Vector)
  360. for _, val := range qrs {
  361. containerMetric, err := NewContainerMetricFrom(val, defaultClusterID)
  362. if err != nil {
  363. return nil, err
  364. }
  365. containerData[containerMetric.Key()] = val.Data
  366. }
  367. return containerData, nil
  368. }
  369. func GetNormalizedContainerMetricVectors(qrs []*source.QueryResult, normalizationValues []*util.Vector, defaultClusterID string) (map[string][]*util.Vector, error) {
  370. containerData := make(map[string][]*util.Vector)
  371. for _, val := range qrs {
  372. containerMetric, err := NewContainerMetricFromResult(val, defaultClusterID)
  373. if err != nil {
  374. return nil, err
  375. }
  376. containerData[containerMetric.Key()] = util.NormalizeVectorByVector(val.Values, normalizationValues)
  377. }
  378. return containerData, nil
  379. }
  380. func getCost[T any](qrs []*T, nodeFunc func(*T) string, dataFunc func(*T) []*util.Vector) (map[string][]*util.Vector, error) {
  381. toReturn := make(map[string][]*util.Vector)
  382. for _, val := range qrs {
  383. instance := nodeFunc(val)
  384. if instance == "" {
  385. return toReturn, fmt.Errorf("missing node field")
  386. }
  387. toReturn[instance] = dataFunc(val)
  388. }
  389. return toReturn, nil
  390. }
  391. func cpuCostNode(res *source.NodeCPUPricePerHrResult) string {
  392. return res.Node
  393. }
  394. func cpuCostData(res *source.NodeCPUPricePerHrResult) []*util.Vector {
  395. return res.Data
  396. }
  397. func ramCostNode(res *source.NodeRAMPricePerGiBHrResult) string {
  398. return res.Node
  399. }
  400. func ramCostData(res *source.NodeRAMPricePerGiBHrResult) []*util.Vector {
  401. return res.Data
  402. }
  403. func gpuCostNode(res *source.NodeGPUPricePerHrResult) string {
  404. return res.Node
  405. }
  406. func gpuCostData(res *source.NodeGPUPricePerHrResult) []*util.Vector {
  407. return res.Data
  408. }
  409. func parsePodLabels(qrs []*source.PodLabelsResult) (map[string]map[string]string, error) {
  410. podLabels := map[string]map[string]string{}
  411. for _, result := range qrs {
  412. pod := result.Pod
  413. if pod == "" {
  414. return podLabels, errors.New("missing pod field")
  415. }
  416. if _, ok := podLabels[pod]; ok {
  417. podLabels[pod] = result.Labels
  418. } else {
  419. podLabels[pod] = map[string]string{}
  420. podLabels[pod] = result.Labels
  421. }
  422. }
  423. return podLabels, nil
  424. }