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  }