| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package vsphere |
| 4 | |
| 5 | import ( |
| 6 | "github.com/netdata/netdata/go/plugins/pkg/metrix" |
| 7 | rs "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/vsphere/resources" |
| 8 | scrapepkg "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/vsphere/scrape" |
| 9 | "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/oldmetrix" |
| 10 | ) |
| 11 | |
| 12 | const ( |
| 13 | vsanClusterSpaceUsageTotalMetric = "vsan_cluster_space_usage_total" |
| 14 | vsanClusterSpaceUsageFreeMetric = "vsan_cluster_space_usage_free" |
| 15 | vsanClusterSpaceUsageUsedMetric = "vsan_cluster_space_usage_used" |
| 16 | vsanClusterSpaceUtilizationUsedMetric = "vsan_cluster_space_utilization_used" |
| 17 | vsanClusterHealthStatusGreenMetric = "vsan_cluster_health_status_green" |
| 18 | vsanClusterHealthStatusYellowMetric = "vsan_cluster_health_status_yellow" |
| 19 | vsanClusterHealthStatusRedMetric = "vsan_cluster_health_status_red" |
| 20 | vsanClusterHealthStatusUnknownMetric = "vsan_cluster_health_status_unknown" |
| 21 | vsanClusterOperationsReadMetric = "vsan_cluster_operations_read" |
| 22 | vsanClusterOperationsWriteMetric = "vsan_cluster_operations_write" |
| 23 | vsanClusterThroughputReadMetric = "vsan_cluster_throughput_read" |
| 24 | vsanClusterThroughputWriteMetric = "vsan_cluster_throughput_write" |
| 25 | vsanClusterLatencyReadMetric = "vsan_cluster_latency_read" |
| 26 | vsanClusterLatencyWriteMetric = "vsan_cluster_latency_write" |
| 27 | vsanClusterCongestionsMetric = "vsan_cluster_congestions" |
| 28 | vsanHostOperationsReadMetric = "vsan_host_operations_read" |
| 29 | vsanHostOperationsWriteMetric = "vsan_host_operations_write" |
| 30 | vsanHostThroughputReadMetric = "vsan_host_throughput_read" |
| 31 | vsanHostThroughputWriteMetric = "vsan_host_throughput_write" |
| 32 | vsanHostLatencyReadMetric = "vsan_host_latency_read" |
| 33 | vsanHostLatencyWriteMetric = "vsan_host_latency_write" |
| 34 | vsanHostCongestionsMetric = "vsan_host_congestions" |
| 35 | vsanHostCacheHitRateMetric = "vsan_host_cache_hit_rate" |
| 36 | vsanVMOperationsReadMetric = "vsan_vm_operations_read" |
| 37 | vsanVMOperationsWriteMetric = "vsan_vm_operations_write" |
| 38 | vsanVMThroughputReadMetric = "vsan_vm_throughput_read" |
| 39 | vsanVMThroughputWriteMetric = "vsan_vm_throughput_write" |
| 40 | vsanVMLatencyReadMetric = "vsan_vm_latency_read" |
| 41 | vsanVMLatencyWriteMetric = "vsan_vm_latency_write" |
| 42 | vsanUUIDLabel = "vsan_uuid" |
| 43 | vsanNodeUUIDLabel = "vsan_node_uuid" |
| 44 | vmInstanceUUIDLabel = "vm_instance_uuid" |
| 45 | ) |
| 46 | |
| 47 | func (c *Collector) writeVSANMetrics() { |
| 48 | if !c.CollectVSAN || c.resources == nil || c.vsanMetrics == nil { |
| 49 | return |
| 50 | } |
| 51 | c.writeVSANClusterSpaceMetrics() |
| 52 | c.writeVSANClusterHealthMetrics() |
| 53 | c.writeVSANClusterPerformanceMetrics() |
| 54 | c.writeVSANHostPerformanceMetrics() |
| 55 | c.writeVSANVMPerformanceMetrics() |
| 56 | } |
| 57 | |
| 58 | func (c *Collector) vsanResources() (rs.Clusters, rs.Hosts, rs.VMs) { |
| 59 | clusters := make(rs.Clusters) |
| 60 | hosts := make(rs.Hosts) |
| 61 | vms := make(rs.VMs) |
| 62 | |
| 63 | clusterMatcher := c.vsanClusterMatcher |
| 64 | hostMatcher := c.vsanHostMatcher |
| 65 | vmMatcher := c.vsanVMMatcher |
| 66 | |
| 67 | selectedClusters := make(map[string]bool) |
| 68 | for _, cluster := range sortedClusters(c.resources.Clusters) { |
| 69 | if !cluster.VSANEnabled || (clusterMatcher != nil && !clusterMatcher.Match(cluster)) { |
| 70 | continue |
| 71 | } |
| 72 | clusters[cluster.ID] = cluster |
| 73 | selectedClusters[cluster.ID] = true |
| 74 | } |
| 75 | |
| 76 | for _, host := range sortedHosts(c.resources.Hosts) { |
| 77 | if !selectedClusters[host.Hier.Cluster.ID] || host.VSANNodeUUID == "" || (hostMatcher != nil && !hostMatcher.Match(host)) { |
| 78 | continue |
| 79 | } |
| 80 | hosts[host.ID] = host |
| 81 | } |
| 82 | |
| 83 | for _, vm := range sortedVMs(c.resources.VMs) { |
| 84 | if !selectedClusters[vm.Hier.Cluster.ID] || vm.InstanceUUID == "" || (vmMatcher != nil && !vmMatcher.Match(vm)) { |
| 85 | continue |
| 86 | } |
| 87 | vms[vm.ID] = vm |
| 88 | } |
| 89 | |
| 90 | return clusters, hosts, vms |
| 91 | } |
| 92 | |
| 93 | func (c *Collector) writeVSANClusterSpaceMetrics() { |
| 94 | for _, id := range sortedMapKeys(c.vsanMetrics.Space) { |
| 95 | cluster := c.resources.Clusters.Get(id) |
| 96 | if cluster == nil { |
| 97 | continue |
| 98 | } |
| 99 | space := c.vsanMetrics.Space[id] |
| 100 | used := max(space.Total-space.Free, 0) |
| 101 | labels := c.labelSet(c.vsanClusterLabels(cluster)) |
| 102 | c.observeGaugeFloat(vsanClusterSpaceUsageTotalMetric, float64(space.Total), labels) |
| 103 | c.observeGaugeFloat(vsanClusterSpaceUsageFreeMetric, float64(space.Free), labels) |
| 104 | c.observeGaugeFloat(vsanClusterSpaceUsageUsedMetric, float64(used), labels) |
| 105 | if space.Total > 0 { |
| 106 | c.observeGaugeFloat(vsanClusterSpaceUtilizationUsedMetric, float64(used)/float64(space.Total)*scaledPercent, labels) |
| 107 | } else { |
| 108 | c.observeGaugeFloat(vsanClusterSpaceUtilizationUsedMetric, 0, labels) |
| 109 | } |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | func (c *Collector) writeVSANClusterHealthMetrics() { |
| 114 | for _, id := range sortedMapKeys(c.vsanMetrics.Health) { |
| 115 | cluster := c.resources.Clusters.Get(id) |
| 116 | if cluster == nil { |
| 117 | continue |
| 118 | } |
| 119 | health := c.vsanMetrics.Health[id] |
| 120 | labels := c.labelSet(c.vsanClusterLabels(cluster)) |
| 121 | c.observeGaugeFloat(vsanClusterHealthStatusGreenMetric, float64(oldmetrix.Bool(health == "green")), labels) |
| 122 | c.observeGaugeFloat(vsanClusterHealthStatusYellowMetric, float64(oldmetrix.Bool(health == "yellow")), labels) |
| 123 | c.observeGaugeFloat(vsanClusterHealthStatusRedMetric, float64(oldmetrix.Bool(health == "red")), labels) |
| 124 | c.observeGaugeFloat(vsanClusterHealthStatusUnknownMetric, float64(oldmetrix.Bool(health != "green" && health != "yellow" && health != "red")), labels) |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | func (c *Collector) writeVSANClusterPerformanceMetrics() { |
| 129 | for _, id := range sortedMapKeys(c.vsanMetrics.Clusters) { |
| 130 | cluster := c.resources.Clusters.Get(id) |
| 131 | if cluster == nil { |
| 132 | continue |
| 133 | } |
| 134 | labels := c.labelSet(c.vsanClusterLabels(cluster)) |
| 135 | writeVSANPerformanceValues(c, labels, c.vsanMetrics.Clusters[id], vsanClusterPerfMetricByName) |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | func (c *Collector) writeVSANHostPerformanceMetrics() { |
| 140 | for _, id := range sortedMapKeys(c.vsanMetrics.Hosts) { |
| 141 | host := c.resources.Hosts.Get(id) |
| 142 | if host == nil { |
| 143 | continue |
| 144 | } |
| 145 | labels := c.labelSet(c.vsanHostLabels(host)) |
| 146 | writeVSANPerformanceValues(c, labels, c.vsanMetrics.Hosts[id], vsanHostPerfMetricByName) |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | func (c *Collector) writeVSANVMPerformanceMetrics() { |
| 151 | for _, id := range sortedMapKeys(c.vsanMetrics.VMs) { |
| 152 | vm := c.resources.VMs.Get(id) |
| 153 | if vm == nil { |
| 154 | continue |
| 155 | } |
| 156 | labels := c.labelSet(c.vsanVMLabels(vm)) |
| 157 | writeVSANPerformanceValues(c, labels, c.vsanMetrics.VMs[id], vsanVMPerfMetricByName) |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | func writeVSANPerformanceValues(c *Collector, labels metrix.LabelSet, values scrapepkg.VSANEntityMetrics, metricByName map[string]string) { |
| 162 | for _, name := range sortedMapKeys(values) { |
| 163 | metricName := metricByName[name] |
| 164 | if metricName == "" { |
| 165 | continue |
| 166 | } |
| 167 | c.observeGaugeFloat(metricName, values[name], labels) |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | var vsanClusterPerfMetricByName = map[string]string{ |
| 172 | "read_operations": vsanClusterOperationsReadMetric, |
| 173 | "write_operations": vsanClusterOperationsWriteMetric, |
| 174 | "read_throughput": vsanClusterThroughputReadMetric, |
| 175 | "write_throughput": vsanClusterThroughputWriteMetric, |
| 176 | "read_latency": vsanClusterLatencyReadMetric, |
| 177 | "write_latency": vsanClusterLatencyWriteMetric, |
| 178 | "congestions": vsanClusterCongestionsMetric, |
| 179 | } |
| 180 | |
| 181 | var vsanHostPerfMetricByName = map[string]string{ |
| 182 | "read_operations": vsanHostOperationsReadMetric, |
| 183 | "write_operations": vsanHostOperationsWriteMetric, |
| 184 | "read_throughput": vsanHostThroughputReadMetric, |
| 185 | "write_throughput": vsanHostThroughputWriteMetric, |
| 186 | "read_latency": vsanHostLatencyReadMetric, |
| 187 | "write_latency": vsanHostLatencyWriteMetric, |
| 188 | "congestions": vsanHostCongestionsMetric, |
| 189 | "cache_hit_rate": vsanHostCacheHitRateMetric, |
| 190 | } |
| 191 | |
| 192 | var vsanVMPerfMetricByName = map[string]string{ |
| 193 | "read_operations": vsanVMOperationsReadMetric, |
| 194 | "write_operations": vsanVMOperationsWriteMetric, |
| 195 | "read_throughput": vsanVMThroughputReadMetric, |
| 196 | "write_throughput": vsanVMThroughputWriteMetric, |
| 197 | "read_latency": vsanVMLatencyReadMetric, |
| 198 | "write_latency": vsanVMLatencyWriteMetric, |
| 199 | } |
| 200 | |
| 201 | func (c *Collector) vsanClusterLabels(cluster *rs.Cluster) []metrix.Label { |
| 202 | return c.v2MetricLabels(cluster.ID, []metrix.Label{ |
| 203 | {Key: "datacenter", Value: cluster.Hier.DC.Name}, |
| 204 | {Key: "cluster", Value: cluster.Name}, |
| 205 | {Key: vsanUUIDLabel, Value: cluster.VSANUUID}, |
| 206 | }, cluster.Labels) |
| 207 | } |
| 208 | |
| 209 | func (c *Collector) vsanHostLabels(host *rs.Host) []metrix.Label { |
| 210 | return c.v2MetricLabels(host.ID, []metrix.Label{ |
| 211 | {Key: "datacenter", Value: host.Hier.DC.Name}, |
| 212 | {Key: "cluster", Value: getHostClusterName(host)}, |
| 213 | {Key: "host", Value: host.Name}, |
| 214 | {Key: vsanNodeUUIDLabel, Value: host.VSANNodeUUID}, |
| 215 | }, host.Labels) |
| 216 | } |
| 217 | |
| 218 | func (c *Collector) vsanVMLabels(vm *rs.VM) []metrix.Label { |
| 219 | return c.v2MetricLabels(vm.ID, []metrix.Label{ |
| 220 | {Key: "datacenter", Value: vm.Hier.DC.Name}, |
| 221 | {Key: "cluster", Value: getVMClusterName(vm)}, |
| 222 | {Key: "host", Value: vm.Hier.Host.Name}, |
| 223 | {Key: "vm", Value: vm.Name}, |
| 224 | {Key: vmInstanceUUIDLabel, Value: vm.InstanceUUID}, |
| 225 | }, vm.Labels) |
| 226 | } |