metrics.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*
  2. Copyright 2018 The Kubernetes Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package metrics
  14. import (
  15. "sync"
  16. "github.com/prometheus/client_golang/prometheus"
  17. "k8s.io/apimachinery/pkg/labels"
  18. corelisters "k8s.io/client-go/listers/core/v1"
  19. "k8s.io/klog"
  20. "k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
  21. "k8s.io/kubernetes/pkg/controller/volume/attachdetach/util"
  22. "k8s.io/kubernetes/pkg/volume"
  23. volumeutil "k8s.io/kubernetes/pkg/volume/util"
  24. )
  25. const pluginNameNotAvailable = "N/A"
  26. var (
  27. inUseVolumeMetricDesc = prometheus.NewDesc(
  28. prometheus.BuildFQName("", "storage_count", "attachable_volumes_in_use"),
  29. "Measure number of volumes in use",
  30. []string{"node", "volume_plugin"}, nil)
  31. totalVolumesMetricDesc = prometheus.NewDesc(
  32. prometheus.BuildFQName("", "attachdetach_controller", "total_volumes"),
  33. "Number of volumes in A/D Controller",
  34. []string{"plugin_name", "state"}, nil)
  35. forcedDetachMetricCounter = prometheus.NewCounter(
  36. prometheus.CounterOpts{
  37. Name: "attachdetach_controller_forced_detaches",
  38. Help: "Number of times the A/D Controller performed a forced detach"})
  39. )
  40. var registerMetrics sync.Once
  41. // Register registers metrics in A/D Controller.
  42. func Register(pvcLister corelisters.PersistentVolumeClaimLister,
  43. pvLister corelisters.PersistentVolumeLister,
  44. podLister corelisters.PodLister,
  45. asw cache.ActualStateOfWorld,
  46. dsw cache.DesiredStateOfWorld,
  47. pluginMgr *volume.VolumePluginMgr) {
  48. registerMetrics.Do(func() {
  49. prometheus.MustRegister(newAttachDetachStateCollector(pvcLister,
  50. podLister,
  51. pvLister,
  52. asw,
  53. dsw,
  54. pluginMgr))
  55. prometheus.MustRegister(forcedDetachMetricCounter)
  56. })
  57. }
  58. type attachDetachStateCollector struct {
  59. pvcLister corelisters.PersistentVolumeClaimLister
  60. podLister corelisters.PodLister
  61. pvLister corelisters.PersistentVolumeLister
  62. asw cache.ActualStateOfWorld
  63. dsw cache.DesiredStateOfWorld
  64. volumePluginMgr *volume.VolumePluginMgr
  65. }
  66. // volumeCount is a map of maps used as a counter, e.g.:
  67. // node 172.168.1.100.ec2.internal has 10 EBS and 3 glusterfs PVC in use:
  68. // {"172.168.1.100.ec2.internal": {"aws-ebs": 10, "glusterfs": 3}}
  69. // state actual_state_of_world contains a total of 10 EBS volumes:
  70. // {"actual_state_of_world": {"aws-ebs": 10}}
  71. type volumeCount map[string]map[string]int64
  72. func (v volumeCount) add(typeKey, counterKey string) {
  73. count, ok := v[typeKey]
  74. if !ok {
  75. count = map[string]int64{}
  76. }
  77. count[counterKey]++
  78. v[typeKey] = count
  79. }
  80. func newAttachDetachStateCollector(
  81. pvcLister corelisters.PersistentVolumeClaimLister,
  82. podLister corelisters.PodLister,
  83. pvLister corelisters.PersistentVolumeLister,
  84. asw cache.ActualStateOfWorld,
  85. dsw cache.DesiredStateOfWorld,
  86. pluginMgr *volume.VolumePluginMgr) *attachDetachStateCollector {
  87. return &attachDetachStateCollector{pvcLister, podLister, pvLister, asw, dsw, pluginMgr}
  88. }
  89. // Check if our collector implements necessary collector interface
  90. var _ prometheus.Collector = &attachDetachStateCollector{}
  91. func (collector *attachDetachStateCollector) Describe(ch chan<- *prometheus.Desc) {
  92. ch <- inUseVolumeMetricDesc
  93. ch <- totalVolumesMetricDesc
  94. }
  95. func (collector *attachDetachStateCollector) Collect(ch chan<- prometheus.Metric) {
  96. nodeVolumeMap := collector.getVolumeInUseCount()
  97. for nodeName, pluginCount := range nodeVolumeMap {
  98. for pluginName, count := range pluginCount {
  99. metric, err := prometheus.NewConstMetric(inUseVolumeMetricDesc,
  100. prometheus.GaugeValue,
  101. float64(count),
  102. string(nodeName),
  103. pluginName)
  104. if err != nil {
  105. klog.Warningf("Failed to create metric : %v", err)
  106. }
  107. ch <- metric
  108. }
  109. }
  110. stateVolumeMap := collector.getTotalVolumesCount()
  111. for stateName, pluginCount := range stateVolumeMap {
  112. for pluginName, count := range pluginCount {
  113. metric, err := prometheus.NewConstMetric(totalVolumesMetricDesc,
  114. prometheus.GaugeValue,
  115. float64(count),
  116. pluginName,
  117. string(stateName))
  118. if err != nil {
  119. klog.Warningf("Failed to create metric : %v", err)
  120. }
  121. ch <- metric
  122. }
  123. }
  124. }
  125. func (collector *attachDetachStateCollector) getVolumeInUseCount() volumeCount {
  126. pods, err := collector.podLister.List(labels.Everything())
  127. if err != nil {
  128. klog.Errorf("Error getting pod list")
  129. return nil
  130. }
  131. nodeVolumeMap := make(volumeCount)
  132. for _, pod := range pods {
  133. if len(pod.Spec.Volumes) <= 0 {
  134. continue
  135. }
  136. if pod.Spec.NodeName == "" {
  137. continue
  138. }
  139. for _, podVolume := range pod.Spec.Volumes {
  140. volumeSpec, err := util.CreateVolumeSpec(podVolume, pod.Namespace, collector.pvcLister, collector.pvLister)
  141. if err != nil {
  142. continue
  143. }
  144. volumePlugin, err := collector.volumePluginMgr.FindPluginBySpec(volumeSpec)
  145. if err != nil {
  146. continue
  147. }
  148. pluginName := volumeutil.GetFullQualifiedPluginNameForVolume(volumePlugin.GetPluginName(), volumeSpec)
  149. nodeVolumeMap.add(pod.Spec.NodeName, pluginName)
  150. }
  151. }
  152. return nodeVolumeMap
  153. }
  154. func (collector *attachDetachStateCollector) getTotalVolumesCount() volumeCount {
  155. stateVolumeMap := make(volumeCount)
  156. for _, v := range collector.dsw.GetVolumesToAttach() {
  157. if plugin, err := collector.volumePluginMgr.FindPluginBySpec(v.VolumeSpec); err == nil {
  158. pluginName := pluginNameNotAvailable
  159. if plugin != nil {
  160. pluginName = volumeutil.GetFullQualifiedPluginNameForVolume(plugin.GetPluginName(), v.VolumeSpec)
  161. }
  162. stateVolumeMap.add("desired_state_of_world", pluginName)
  163. }
  164. }
  165. for _, v := range collector.asw.GetAttachedVolumes() {
  166. if plugin, err := collector.volumePluginMgr.FindPluginBySpec(v.VolumeSpec); err == nil {
  167. pluginName := pluginNameNotAvailable
  168. if plugin != nil {
  169. pluginName = volumeutil.GetFullQualifiedPluginNameForVolume(plugin.GetPluginName(), v.VolumeSpec)
  170. }
  171. stateVolumeMap.add("actual_state_of_world", pluginName)
  172. }
  173. }
  174. return stateVolumeMap
  175. }
  176. // RecordForcedDetachMetric register a forced detach metric.
  177. func RecordForcedDetachMetric() {
  178. forcedDetachMetricCounter.Inc()
  179. }