github.com/netdata/go.d.plugin@v0.58.1/modules/vsphere/scrape/scrape.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package scrape 4 5 import ( 6 "fmt" 7 "strconv" 8 "strings" 9 "sync" 10 "time" 11 12 rs "github.com/netdata/go.d.plugin/modules/vsphere/resources" 13 14 "github.com/netdata/go.d.plugin/logger" 15 "github.com/vmware/govmomi/performance" 16 "github.com/vmware/govmomi/vim25/types" 17 ) 18 19 type Client interface { 20 Version() string 21 PerformanceMetrics([]types.PerfQuerySpec) ([]performance.EntityMetric, error) 22 } 23 24 func New(client Client) *Scraper { 25 v := &Scraper{Client: client} 26 v.calcMaxQuery() 27 return v 28 } 29 30 type Scraper struct { 31 *logger.Logger 32 Client 33 maxQuery int 34 } 35 36 // Default settings for vCenter 6.5 and above is 256, prior versions of vCenter have this set to 64. 37 func (c *Scraper) calcMaxQuery() { 38 major, minor, err := parseVersion(c.Version()) 39 if err != nil || major < 6 || minor == 0 { 40 c.maxQuery = 64 41 return 42 } 43 c.maxQuery = 256 44 } 45 46 func (c Scraper) ScrapeHosts(hosts rs.Hosts) []performance.EntityMetric { 47 t := time.Now() 48 pqs := newHostsPerfQuerySpecs(hosts) 49 ms := c.scrapeMetrics(pqs) 50 c.Debugf("scraping : scraped metrics for %d/%d hosts, process took %s", 51 len(ms), 52 len(hosts), 53 time.Since(t), 54 ) 55 return ms 56 } 57 58 func (c Scraper) ScrapeVMs(vms rs.VMs) []performance.EntityMetric { 59 t := time.Now() 60 pqs := newVMsPerfQuerySpecs(vms) 61 ms := c.scrapeMetrics(pqs) 62 c.Debugf("scraping : scraped metrics for %d/%d vms, process took %s", 63 len(ms), 64 len(vms), 65 time.Since(t), 66 ) 67 return ms 68 } 69 70 func (c Scraper) scrapeMetrics(pqs []types.PerfQuerySpec) []performance.EntityMetric { 71 tc := newThrottledCaller(5) 72 var ms []performance.EntityMetric 73 lock := &sync.Mutex{} 74 75 chunks := chunkify(pqs, c.maxQuery) 76 for _, chunk := range chunks { 77 pqs := chunk 78 job := func() { 79 c.scrape(&ms, lock, pqs) 80 } 81 tc.call(job) 82 } 83 tc.wait() 84 85 return ms 86 } 87 88 func (c Scraper) scrape(metrics *[]performance.EntityMetric, lock *sync.Mutex, pqs []types.PerfQuerySpec) { 89 m, err := c.PerformanceMetrics(pqs) 90 if err != nil { 91 c.Error(err) 92 return 93 } 94 95 lock.Lock() 96 *metrics = append(*metrics, m...) 97 lock.Unlock() 98 } 99 100 func chunkify(pqs []types.PerfQuerySpec, chunkSize int) (chunks [][]types.PerfQuerySpec) { 101 for i := 0; i < len(pqs); i += chunkSize { 102 end := i + chunkSize 103 if end > len(pqs) { 104 end = len(pqs) 105 } 106 chunks = append(chunks, pqs[i:end]) 107 } 108 return chunks 109 } 110 111 const ( 112 pqsMaxSample = 1 113 pqsIntervalID = 20 114 pqsFormat = "normal" 115 ) 116 117 func newHostsPerfQuerySpecs(hosts rs.Hosts) []types.PerfQuerySpec { 118 pqs := make([]types.PerfQuerySpec, 0, len(hosts)) 119 for _, host := range hosts { 120 pq := types.PerfQuerySpec{ 121 Entity: host.Ref, 122 MaxSample: pqsMaxSample, 123 MetricId: host.MetricList, 124 IntervalId: pqsIntervalID, 125 Format: pqsFormat, 126 } 127 pqs = append(pqs, pq) 128 } 129 return pqs 130 } 131 132 func newVMsPerfQuerySpecs(vms rs.VMs) []types.PerfQuerySpec { 133 pqs := make([]types.PerfQuerySpec, 0, len(vms)) 134 for _, vm := range vms { 135 pq := types.PerfQuerySpec{ 136 Entity: vm.Ref, 137 MaxSample: pqsMaxSample, 138 MetricId: vm.MetricList, 139 IntervalId: pqsIntervalID, 140 Format: pqsFormat, 141 } 142 pqs = append(pqs, pq) 143 } 144 return pqs 145 } 146 147 func parseVersion(version string) (major, minor int, err error) { 148 parts := strings.Split(version, ".") 149 if len(parts) < 2 { 150 return 0, 0, fmt.Errorf("unparsable version string : %s", version) 151 } 152 if major, err = strconv.Atoi(parts[0]); err != nil { 153 return 0, 0, err 154 } 155 if minor, err = strconv.Atoi(parts[1]); err != nil { 156 return 0, 0, err 157 } 158 return major, minor, nil 159 }