github.com/netdata/go.d.plugin@v0.58.1/modules/vsphere/charts.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package vsphere
     4  
     5  import (
     6  	"fmt"
     7  	"strings"
     8  
     9  	"github.com/netdata/go.d.plugin/agent/module"
    10  	rs "github.com/netdata/go.d.plugin/modules/vsphere/resources"
    11  )
    12  
    13  const (
    14  	prioVMCPUUtilization = module.Priority + iota
    15  	prioVmMemoryUtilization
    16  	prioVmMemoryUsage
    17  	prioVmMemorySwapUsage
    18  	prioVmMemorySwapIO
    19  	prioVmDiskIO
    20  	prioVmDiskMaxLatency
    21  	prioVmNetworkTraffic
    22  	prioVmNetworkPackets
    23  	prioVmNetworkDrops
    24  	prioVmOverallStatus
    25  	prioVmSystemUptime
    26  
    27  	prioHostCPUUtilization
    28  	prioHostMemoryUtilization
    29  	prioHostMemoryUsage
    30  	prioHostMemorySwapIO
    31  	prioHostDiskIO
    32  	prioHostDiskMaxLatency
    33  	prioHostNetworkTraffic
    34  	prioHostNetworkPackets
    35  	prioHostNetworkDrops
    36  	prioHostNetworkErrors
    37  	prioHostOverallStatus
    38  	prioHostSystemUptime
    39  )
    40  
    41  var (
    42  	vmChartsTmpl = module.Charts{
    43  		vmCPUUtilizationChartTmpl.Copy(),
    44  
    45  		vmMemoryUtilizationChartTmpl.Copy(),
    46  		vmMemoryUsageChartTmpl.Copy(),
    47  		vmMemorySwapUsageChartTmpl.Copy(),
    48  		vmMemorySwapIOChartTmpl.Copy(),
    49  
    50  		vmDiskIOChartTmpl.Copy(),
    51  		vmDiskMaxLatencyChartTmpl.Copy(),
    52  
    53  		vmNetworkTrafficChartTmpl.Copy(),
    54  		vmNetworkPacketsChartTmpl.Copy(),
    55  		vmNetworkDropsChartTmpl.Copy(),
    56  
    57  		vmOverallStatusChartTmpl.Copy(),
    58  
    59  		vmSystemUptimeChartTmpl.Copy(),
    60  	}
    61  
    62  	vmCPUUtilizationChartTmpl = module.Chart{
    63  		ID:       "%s_cpu_utilization",
    64  		Title:    "Virtual Machine CPU utilization",
    65  		Units:    "percentage",
    66  		Fam:      "vms cpu",
    67  		Ctx:      "vsphere.vm_cpu_utilization",
    68  		Priority: prioVMCPUUtilization,
    69  		Dims: module.Dims{
    70  			{ID: "%s_cpu.usage.average", Name: "used", Div: 100},
    71  		},
    72  	}
    73  
    74  	// Ref: https://www.vmware.com/support/developer/converter-sdk/conv51_apireference/memory_counters.html
    75  	vmMemoryUtilizationChartTmpl = module.Chart{
    76  		ID:       "%s_mem_utilization",
    77  		Title:    "Virtual Machine memory utilization",
    78  		Units:    "percentage",
    79  		Fam:      "vms mem",
    80  		Ctx:      "vsphere.vm_mem_utilization",
    81  		Priority: prioVmMemoryUtilization,
    82  		Dims: module.Dims{
    83  			{ID: "%s_mem.usage.average", Name: "used", Div: 100},
    84  		},
    85  	}
    86  	vmMemoryUsageChartTmpl = module.Chart{
    87  		ID:       "%s_mem_usage",
    88  		Title:    "Virtual Machine memory usage",
    89  		Units:    "KiB",
    90  		Fam:      "vms mem",
    91  		Ctx:      "vsphere.vm_mem_usage",
    92  		Priority: prioVmMemoryUsage,
    93  		Dims: module.Dims{
    94  			{ID: "%s_mem.granted.average", Name: "granted"},
    95  			{ID: "%s_mem.consumed.average", Name: "consumed"},
    96  			{ID: "%s_mem.active.average", Name: "active"},
    97  			{ID: "%s_mem.shared.average", Name: "shared"},
    98  		},
    99  	}
   100  	vmMemorySwapUsageChartTmpl = module.Chart{
   101  		ID:       "%s_mem_swap_usage",
   102  		Title:    "Virtual Machine VMKernel memory swap usage",
   103  		Units:    "KiB",
   104  		Fam:      "vms mem",
   105  		Ctx:      "vsphere.vm_mem_swap_usage",
   106  		Priority: prioVmMemorySwapUsage,
   107  		Dims: module.Dims{
   108  			{ID: "%s_mem.swapped.average", Name: "swapped"},
   109  		},
   110  	}
   111  	vmMemorySwapIOChartTmpl = module.Chart{
   112  		ID:       "%s_mem_swap_io_rate",
   113  		Title:    "Virtual Machine VMKernel memory swap IO",
   114  		Units:    "KiB/s",
   115  		Fam:      "vms mem",
   116  		Ctx:      "vsphere.vm_mem_swap_io",
   117  		Type:     module.Area,
   118  		Priority: prioVmMemorySwapIO,
   119  		Dims: module.Dims{
   120  			{ID: "%s_mem.swapinRate.average", Name: "in"},
   121  			{ID: "%s_mem.swapoutRate.average", Name: "out"},
   122  		},
   123  	}
   124  
   125  	vmDiskIOChartTmpl = module.Chart{
   126  		ID:       "%s_disk_io",
   127  		Title:    "Virtual Machine disk IO",
   128  		Units:    "KiB/s",
   129  		Fam:      "vms disk",
   130  		Ctx:      "vsphere.vm_disk_io",
   131  		Type:     module.Area,
   132  		Priority: prioVmDiskIO,
   133  		Dims: module.Dims{
   134  			{ID: "%s_disk.read.average", Name: "read"},
   135  			{ID: "%s_disk.write.average", Name: "write", Mul: -1},
   136  		},
   137  	}
   138  	vmDiskMaxLatencyChartTmpl = module.Chart{
   139  		ID:       "%s_disk_max_latency",
   140  		Title:    "Virtual Machine disk max latency",
   141  		Units:    "milliseconds",
   142  		Fam:      "vms disk",
   143  		Ctx:      "vsphere.vm_disk_max_latency",
   144  		Priority: prioVmDiskMaxLatency,
   145  		Dims: module.Dims{
   146  			{ID: "%s_disk.maxTotalLatency.latest", Name: "latency"},
   147  		},
   148  	}
   149  
   150  	vmNetworkTrafficChartTmpl = module.Chart{
   151  		ID:       "%s_net_traffic",
   152  		Title:    "Virtual Machine network traffic",
   153  		Units:    "KiB/s",
   154  		Fam:      "vms net",
   155  		Ctx:      "vsphere.vm_net_traffic",
   156  		Type:     module.Area,
   157  		Priority: prioVmNetworkTraffic,
   158  		Dims: module.Dims{
   159  			{ID: "%s_net.bytesRx.average", Name: "received"},
   160  			{ID: "%s_net.bytesTx.average", Name: "sent", Mul: -1},
   161  		},
   162  	}
   163  	vmNetworkPacketsChartTmpl = module.Chart{
   164  		ID:       "%s_net_packets",
   165  		Title:    "Virtual Machine network packets",
   166  		Units:    "packets",
   167  		Fam:      "vms net",
   168  		Ctx:      "vsphere.vm_net_packets",
   169  		Priority: prioVmNetworkPackets,
   170  		Dims: module.Dims{
   171  			{ID: "%s_net.packetsRx.summation", Name: "received"},
   172  			{ID: "%s_net.packetsTx.summation", Name: "sent", Mul: -1},
   173  		},
   174  	}
   175  	vmNetworkDropsChartTmpl = module.Chart{
   176  		ID:       "%s_net_drops",
   177  		Title:    "Virtual Machine network dropped packets",
   178  		Units:    "drops",
   179  		Fam:      "vms net",
   180  		Ctx:      "vsphere.vm_net_drops",
   181  		Priority: prioVmNetworkDrops,
   182  		Dims: module.Dims{
   183  			{ID: "%s_net.droppedRx.summation", Name: "received"},
   184  			{ID: "%s_net.droppedTx.summation", Name: "sent", Mul: -1},
   185  		},
   186  	}
   187  
   188  	vmOverallStatusChartTmpl = module.Chart{
   189  		ID:       "%s_overall_status",
   190  		Title:    "Virtual Machine overall alarm status",
   191  		Units:    "status",
   192  		Fam:      "vms status",
   193  		Ctx:      "vsphere.vm_overall_status",
   194  		Priority: prioVmOverallStatus,
   195  		Dims: module.Dims{
   196  			{ID: "%s_overall.status.green", Name: "green"},
   197  			{ID: "%s_overall.status.red", Name: "red"},
   198  			{ID: "%s_overall.status.yellow", Name: "yellow"},
   199  			{ID: "%s_overall.status.gray", Name: "gray"},
   200  		},
   201  	}
   202  
   203  	vmSystemUptimeChartTmpl = module.Chart{
   204  		ID:       "%s_system_uptime",
   205  		Title:    "Virtual Machine system uptime",
   206  		Units:    "seconds",
   207  		Fam:      "vms uptime",
   208  		Ctx:      "vsphere.vm_system_uptime",
   209  		Priority: prioVmSystemUptime,
   210  		Dims: module.Dims{
   211  			{ID: "%s_sys.uptime.latest", Name: "uptime"},
   212  		},
   213  	}
   214  )
   215  
   216  var (
   217  	hostChartsTmpl = module.Charts{
   218  		hostCPUUtilizationChartTmpl.Copy(),
   219  
   220  		hostMemUtilizationChartTmpl.Copy(),
   221  		hostMemUsageChartTmpl.Copy(),
   222  		hostMemSwapIOChartTmpl.Copy(),
   223  
   224  		hostDiskIOChartTmpl.Copy(),
   225  		hostDiskMaxLatencyChartTmpl.Copy(),
   226  
   227  		hostNetworkTraffic.Copy(),
   228  		hostNetworkPacketsChartTmpl.Copy(),
   229  		hostNetworkDropsChartTmpl.Copy(),
   230  		hostNetworkErrorsChartTmpl.Copy(),
   231  
   232  		hostOverallStatusChartTmpl.Copy(),
   233  
   234  		hostSystemUptimeChartTmpl.Copy(),
   235  	}
   236  	hostCPUUtilizationChartTmpl = module.Chart{
   237  		ID:       "%s_cpu_usage_total",
   238  		Title:    "ESXi Host CPU utilization",
   239  		Units:    "percentage",
   240  		Fam:      "hosts cpu",
   241  		Ctx:      "vsphere.host_cpu_utilization",
   242  		Priority: prioHostCPUUtilization,
   243  		Dims: module.Dims{
   244  			{ID: "%s_cpu.usage.average", Name: "used", Div: 100},
   245  		},
   246  	}
   247  	hostMemUtilizationChartTmpl = module.Chart{
   248  		ID:       "%s_mem_utilization",
   249  		Title:    "ESXi Host memory utilization",
   250  		Units:    "percentage",
   251  		Fam:      "hosts mem",
   252  		Ctx:      "vsphere.host_mem_utilization",
   253  		Priority: prioHostMemoryUtilization,
   254  		Dims: module.Dims{
   255  			{ID: "%s_mem.usage.average", Name: "used", Div: 100},
   256  		},
   257  	}
   258  	hostMemUsageChartTmpl = module.Chart{
   259  		ID:       "%s_mem_usage",
   260  		Title:    "ESXi Host memory usage",
   261  		Units:    "KiB",
   262  		Fam:      "hosts mem",
   263  		Ctx:      "vsphere.host_mem_usage",
   264  		Priority: prioHostMemoryUsage,
   265  		Dims: module.Dims{
   266  			{ID: "%s_mem.granted.average", Name: "granted"},
   267  			{ID: "%s_mem.consumed.average", Name: "consumed"},
   268  			{ID: "%s_mem.active.average", Name: "active"},
   269  			{ID: "%s_mem.shared.average", Name: "shared"},
   270  			{ID: "%s_mem.sharedcommon.average", Name: "sharedcommon"},
   271  		},
   272  	}
   273  	hostMemSwapIOChartTmpl = module.Chart{
   274  		ID:       "%s_mem_swap_rate",
   275  		Title:    "ESXi Host VMKernel memory swap IO",
   276  		Units:    "KiB/s",
   277  		Fam:      "hosts mem",
   278  		Ctx:      "vsphere.host_mem_swap_io",
   279  		Type:     module.Area,
   280  		Priority: prioHostMemorySwapIO,
   281  		Dims: module.Dims{
   282  			{ID: "%s_mem.swapinRate.average", Name: "in"},
   283  			{ID: "%s_mem.swapoutRate.average", Name: "out"},
   284  		},
   285  	}
   286  
   287  	hostDiskIOChartTmpl = module.Chart{
   288  		ID:       "%s_disk_io",
   289  		Title:    "ESXi Host disk IO",
   290  		Units:    "KiB/s",
   291  		Fam:      "hosts disk",
   292  		Ctx:      "vsphere.host_disk_io",
   293  		Type:     module.Area,
   294  		Priority: prioHostDiskIO,
   295  		Dims: module.Dims{
   296  			{ID: "%s_disk.read.average", Name: "read"},
   297  			{ID: "%s_disk.write.average", Name: "write", Mul: -1},
   298  		},
   299  	}
   300  	hostDiskMaxLatencyChartTmpl = module.Chart{
   301  		ID:       "%s_disk_max_latency",
   302  		Title:    "ESXi Host disk max latency",
   303  		Units:    "milliseconds",
   304  		Fam:      "hosts disk",
   305  		Ctx:      "vsphere.host_disk_max_latency",
   306  		Priority: prioHostDiskMaxLatency,
   307  		Dims: module.Dims{
   308  			{ID: "%s_disk.maxTotalLatency.latest", Name: "latency"},
   309  		},
   310  	}
   311  
   312  	hostNetworkTraffic = module.Chart{
   313  		ID:       "%s_net_traffic",
   314  		Title:    "ESXi Host network traffic",
   315  		Units:    "KiB/s",
   316  		Fam:      "hosts net",
   317  		Ctx:      "vsphere.host_net_traffic",
   318  		Type:     module.Area,
   319  		Priority: prioHostNetworkTraffic,
   320  		Dims: module.Dims{
   321  			{ID: "%s_net.bytesRx.average", Name: "received"},
   322  			{ID: "%s_net.bytesTx.average", Name: "sent", Mul: -1},
   323  		},
   324  	}
   325  	hostNetworkPacketsChartTmpl = module.Chart{
   326  		ID:       "%s_net_packets",
   327  		Title:    "ESXi Host network packets",
   328  		Units:    "packets",
   329  		Fam:      "hosts net",
   330  		Ctx:      "vsphere.host_net_packets",
   331  		Priority: prioHostNetworkPackets,
   332  		Dims: module.Dims{
   333  			{ID: "%s_net.packetsRx.summation", Name: "received"},
   334  			{ID: "%s_net.packetsTx.summation", Name: "sent", Mul: -1},
   335  		},
   336  	}
   337  	hostNetworkDropsChartTmpl = module.Chart{
   338  		ID:       "%s_net_drops_total",
   339  		Title:    "ESXi Host network drops",
   340  		Units:    "drops",
   341  		Fam:      "hosts net",
   342  		Ctx:      "vsphere.host_net_drops",
   343  		Priority: prioHostNetworkDrops,
   344  		Dims: module.Dims{
   345  			{ID: "%s_net.droppedRx.summation", Name: "received"},
   346  			{ID: "%s_net.droppedTx.summation", Name: "sent", Mul: -1},
   347  		},
   348  	}
   349  	hostNetworkErrorsChartTmpl = module.Chart{
   350  		ID:       "%s_net_errors",
   351  		Title:    "ESXi Host network errors",
   352  		Units:    "errors",
   353  		Fam:      "hosts net",
   354  		Ctx:      "vsphere.host_net_errors",
   355  		Priority: prioHostNetworkErrors,
   356  		Dims: module.Dims{
   357  			{ID: "%s_net.errorsRx.summation", Name: "received"},
   358  			{ID: "%s_net.errorsTx.summation", Name: "sent", Mul: -1},
   359  		},
   360  	}
   361  
   362  	hostOverallStatusChartTmpl = module.Chart{
   363  		ID:       "%s_overall_status",
   364  		Title:    "ESXi Host overall alarm status",
   365  		Units:    "status",
   366  		Fam:      "hosts status",
   367  		Ctx:      "vsphere.host_overall_status",
   368  		Priority: prioHostOverallStatus,
   369  		Dims: module.Dims{
   370  			{ID: "%s_overall.status.green", Name: "green"},
   371  			{ID: "%s_overall.status.red", Name: "red"},
   372  			{ID: "%s_overall.status.yellow", Name: "yellow"},
   373  			{ID: "%s_overall.status.gray", Name: "gray"},
   374  		},
   375  	}
   376  	hostSystemUptimeChartTmpl = module.Chart{
   377  		ID:       "%s_system_uptime",
   378  		Title:    "ESXi Host system uptime",
   379  		Units:    "seconds",
   380  		Fam:      "hosts uptime",
   381  		Ctx:      "vsphere.host_system_uptime",
   382  		Priority: prioHostSystemUptime,
   383  		Dims: module.Dims{
   384  			{ID: "%s_sys.uptime.latest", Name: "uptime"},
   385  		},
   386  	}
   387  )
   388  
   389  const failedUpdatesLimit = 10
   390  
   391  func (vs *VSphere) updateCharts() {
   392  	for id, fails := range vs.discoveredHosts {
   393  		if fails >= failedUpdatesLimit {
   394  			vs.removeFromCharts(id)
   395  			delete(vs.charted, id)
   396  			delete(vs.discoveredHosts, id)
   397  			continue
   398  		}
   399  
   400  		host := vs.resources.Hosts.Get(id)
   401  		if host == nil || vs.charted[id] || fails != 0 {
   402  			continue
   403  		}
   404  
   405  		vs.charted[id] = true
   406  		charts := newHostCharts(host)
   407  		if err := vs.Charts().Add(*charts...); err != nil {
   408  			vs.Error(err)
   409  		}
   410  	}
   411  
   412  	for id, fails := range vs.discoveredVMs {
   413  		if fails >= failedUpdatesLimit {
   414  			vs.removeFromCharts(id)
   415  			delete(vs.charted, id)
   416  			delete(vs.discoveredVMs, id)
   417  			continue
   418  		}
   419  
   420  		vm := vs.resources.VMs.Get(id)
   421  		if vm == nil || vs.charted[id] || fails != 0 {
   422  			continue
   423  		}
   424  
   425  		vs.charted[id] = true
   426  		charts := newVMCHarts(vm)
   427  		if err := vs.Charts().Add(*charts...); err != nil {
   428  			vs.Error(err)
   429  		}
   430  	}
   431  }
   432  
   433  func newVMCHarts(vm *rs.VM) *module.Charts {
   434  	charts := vmChartsTmpl.Copy()
   435  
   436  	for _, chart := range *charts {
   437  		chart.ID = fmt.Sprintf(chart.ID, vm.ID)
   438  		chart.Labels = []module.Label{
   439  			{Key: "datacenter", Value: vm.Hier.DC.Name},
   440  			{Key: "cluster", Value: getVMClusterName(vm)},
   441  			{Key: "host", Value: vm.Hier.Host.Name},
   442  			{Key: "vm", Value: vm.Name},
   443  		}
   444  		for _, dim := range chart.Dims {
   445  			dim.ID = fmt.Sprintf(dim.ID, vm.ID)
   446  		}
   447  	}
   448  
   449  	return charts
   450  }
   451  
   452  func getVMClusterName(vm *rs.VM) string {
   453  	if vm.Hier.Cluster.Name == vm.Hier.Host.Name {
   454  		return ""
   455  	}
   456  	return vm.Hier.Cluster.Name
   457  }
   458  
   459  func newHostCharts(host *rs.Host) *module.Charts {
   460  	charts := hostChartsTmpl.Copy()
   461  
   462  	for _, chart := range *charts {
   463  		chart.ID = fmt.Sprintf(chart.ID, host.ID)
   464  		chart.Labels = []module.Label{
   465  			{Key: "datacenter", Value: host.Hier.DC.Name},
   466  			{Key: "cluster", Value: getHostClusterName(host)},
   467  			{Key: "host", Value: host.Name},
   468  		}
   469  
   470  		for _, dim := range chart.Dims {
   471  			dim.ID = fmt.Sprintf(dim.ID, host.ID)
   472  		}
   473  	}
   474  
   475  	return charts
   476  }
   477  
   478  func getHostClusterName(host *rs.Host) string {
   479  	if host.Hier.Cluster.Name == host.Name {
   480  		return ""
   481  	}
   482  	return host.Hier.Cluster.Name
   483  }
   484  
   485  func (vs *VSphere) removeFromCharts(prefix string) {
   486  	for _, c := range *vs.Charts() {
   487  		if strings.HasPrefix(c.ID, prefix) {
   488  			c.MarkRemove()
   489  			c.MarkNotCreated()
   490  		}
   491  	}
   492  }
   493  
   494  //func findMetricSeriesByPrefix(ms []performance.MetricSeries, prefix string) []performance.MetricSeries {
   495  //	from := sort.Search(len(ms), func(i int) bool { return ms[i].Name >= prefix })
   496  //
   497  //	if from == len(ms) || !strings.HasPrefix(ms[from].Name, prefix) {
   498  //		return nil
   499  //	}
   500  //
   501  //	until := from + 1
   502  //	for until < len(ms) && strings.HasPrefix(ms[until].Name, prefix) {
   503  //		until++
   504  //	}
   505  //	return ms[from:until]
   506  //}