master
go 263 lines 8.23 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package vsphere
4
5 import (
6 "context"
7 _ "embed"
8 "fmt"
9 "sync"
10 "time"
11
12 "github.com/vmware/govmomi/performance"
13 mo25 "github.com/vmware/govmomi/vim25/mo"
14 "github.com/vmware/govmomi/vim25/types"
15
16 "github.com/netdata/netdata/go/plugins/pkg/confopt"
17 "github.com/netdata/netdata/go/plugins/pkg/matcher"
18 "github.com/netdata/netdata/go/plugins/pkg/metrix"
19 "github.com/netdata/netdata/go/plugins/pkg/web"
20 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
21 clientpkg "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/vsphere/client"
22 "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/vsphere/match"
23 rs "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/vsphere/resources"
24 scrapepkg "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/vsphere/scrape"
25 )
26
27 //go:embed "config_schema.json"
28 var configSchema string
29
30 //go:embed "charts.yaml"
31 var chartTemplateYAML string
32
33 func init() {
34 collectorapi.Register("vsphere", collectorapi.Creator{
35 JobConfigSchema: configSchema,
36 Defaults: collectorapi.Defaults{
37 UpdateEvery: 20,
38 },
39 CreateV2: func() collectorapi.CollectorV2 { return New() },
40 Config: func() any { return &Config{} },
41 Methods: vsphereMethods,
42 MethodHandler: vsphereMethodHandler,
43 })
44 }
45
46 func New() *Collector {
47 store := metrix.NewCollectorStore()
48 mx := newCollectorMetrics(store)
49
50 return &Collector{
51 Config: Config{
52 HTTPConfig: web.HTTPConfig{
53 ClientConfig: web.ClientConfig{
54 Timeout: confopt.Duration(time.Second * 20),
55 },
56 },
57 DiscoveryInterval: confopt.Duration(time.Minute * 5),
58 HostsInclude: match.HostIncludes{"/*"},
59 VMsInclude: match.VMIncludes{"/*"},
60 DatastoresInclude: match.DatastoreIncludes{"/*"},
61 ClustersInclude: match.ClusterIncludes{"/*"},
62 DatastoreClustersInclude: match.DatastoreClusterIncludes{"/*"},
63 CollectVSAN: false,
64 VSANClustersInclude: match.VSANClusterIncludes{"/*"},
65 VSANHostsInclude: match.VSANHostIncludes{"/*"},
66 VSANVMsInclude: match.VSANVMIncludes{"/*"},
67 },
68 store: store,
69 mx: mx,
70 collectionLock: &sync.RWMutex{},
71 }
72 }
73
74 type Config struct {
75 // Job identity and scheduling.
76 Vnode string `yaml:"vnode,omitempty" json:"vnode"`
77 UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
78 AutoDetectionRetry int `yaml:"autodetection_retry,omitempty" json:"autodetection_retry"`
79 web.HTTPConfig `yaml:",inline" json:""`
80
81 // Inventory discovery and resource selectors.
82 DiscoveryInterval confopt.Duration `yaml:"discovery_interval,omitempty" json:"discovery_interval"`
83 HostsInclude match.HostIncludes `yaml:"host_include,omitempty" json:"host_include"`
84 VMsInclude match.VMIncludes `yaml:"vm_include,omitempty" json:"vm_include"`
85 DatastoresInclude match.DatastoreIncludes `yaml:"datastore_include,omitempty" json:"datastore_include"`
86 ClustersInclude match.ClusterIncludes `yaml:"cluster_include,omitempty" json:"cluster_include"`
87 CollectDatastoreClusters bool `yaml:"collect_datastore_clusters,omitempty" json:"collect_datastore_clusters"`
88 DatastoreClustersInclude match.DatastoreClusterIncludes `yaml:"datastore_cluster_include,omitempty" json:"datastore_cluster_include"`
89 CollectVSAN bool `yaml:"collect_vsan,omitempty" json:"collect_vsan"`
90 VSANClustersInclude match.VSANClusterIncludes `yaml:"vsan_cluster_include,omitempty" json:"vsan_cluster_include"`
91 VSANHostsInclude match.VSANHostIncludes `yaml:"vsan_host_include,omitempty" json:"vsan_host_include"`
92 VSANVMsInclude match.VSANVMIncludes `yaml:"vsan_vm_include,omitempty" json:"vsan_vm_include"`
93
94 // Opt-in label enrichment.
95 TagCategories []string `yaml:"tag_categories,omitempty" json:"tag_categories"`
96 CustomAttributes []string `yaml:"custom_attributes,omitempty" json:"custom_attributes"`
97
98 // Optional cached topology Function data.
99 CollectNetworkTopology bool `yaml:"collect_network_topology,omitempty" json:"collect_network_topology"`
100 }
101
102 type (
103 Collector struct {
104 collectorapi.Base
105 Config `yaml:",inline" json:""`
106
107 store metrix.CollectorStore
108 mx *collectorMetrics
109
110 vsClient *clientpkg.Client
111 discoverer
112 scraper
113 dsPropertyCollector
114 clusterPropertyCollector
115 rpPropertyCollector
116
117 collectionLock *sync.RWMutex
118 resources *rs.Resources
119 discoveryTask *task
120 datastoreClusterMatcher match.DatastoreClusterMatcher
121 vsanClusterMatcher match.VSANClusterMatcher
122 vsanHostMatcher match.VSANHostMatcher
123 vsanVMMatcher match.VSANVMMatcher
124 vsphereTagCategoryMatcher matcher.Matcher
125 customAttributeMatcher matcher.Matcher
126 hostPowerPerfSamples map[string]*hostPowerPerfSample
127 vmPowerPerfSamples map[string]*vmPowerPerfSample
128 vsanMetrics *scrapepkg.VSANMetrics
129 }
130 discoverer interface {
131 Discover() (*rs.Resources, error)
132 }
133 scraper interface {
134 ScrapeHosts(rs.Hosts) []performance.EntityMetric
135 ScrapeVMs(rs.VMs) []performance.EntityMetric
136 ScrapeDatastores(rs.Datastores) []performance.EntityMetric
137 ScrapeClusters(rs.Clusters) []performance.EntityMetric
138 ScrapeVSAN(rs.Clusters, rs.Hosts, rs.VMs) *scrapepkg.VSANMetrics
139 }
140 dsPropertyCollector interface {
141 DatastoresByRef(refs []types.ManagedObjectReference, pathSet ...string) ([]mo25.Datastore, error)
142 }
143 clusterPropertyCollector interface {
144 ClustersByRef(refs []types.ManagedObjectReference, pathSet ...string) ([]mo25.ClusterComputeResource, error)
145 }
146 rpPropertyCollector interface {
147 ResourcePoolsByRef(refs []types.ManagedObjectReference, pathSet ...string) ([]mo25.ResourcePool, error)
148 }
149 )
150
151 func (c *Collector) Configuration() any {
152 return c.Config
153 }
154
155 func (c *Collector) Init(context.Context) error {
156 c.ensureRuntimeState()
157 c.stopDiscoveryTask(true)
158 c.closeClient()
159 c.resetRuntimeStateForInit()
160
161 if err := c.validateConfig(); err != nil {
162 return fmt.Errorf("validate vSphere collector configuration: %w", err)
163 }
164
165 vsClient, err := c.initClient()
166 if err != nil {
167 return fmt.Errorf("create vSphere client: %w", err)
168 }
169 c.vsClient = vsClient
170
171 if err := c.initDiscoverer(vsClient); err != nil {
172 c.closeClient()
173 return fmt.Errorf("create vSphere discoverer from configuration: %w", err)
174 }
175
176 c.initScraper(vsClient)
177
178 if err := c.discoverOnce(); err != nil {
179 c.closeClient()
180 return fmt.Errorf("run initial vSphere discovery: %w", err)
181 }
182
183 c.goDiscovery()
184
185 return nil
186 }
187
188 func (c *Collector) Check(context.Context) error {
189 return nil
190 }
191
192 func (c *Collector) Collect(context.Context) error {
193 c.collectionLock.Lock()
194 defer c.collectionLock.Unlock()
195
196 if err := c.collectLocked(); err != nil {
197 return fmt.Errorf("collect vSphere metrics: %w", err)
198 }
199
200 return nil
201 }
202
203 func (c *Collector) MetricStore() metrix.CollectorStore { return c.store }
204
205 func (c *Collector) ChartTemplateYAML() string { return chartTemplateYAML }
206
207 func (c *Collector) Cleanup(context.Context) {
208 c.stopDiscoveryTask(true)
209 c.closeClient()
210 }
211
212 func (c *Collector) ensureRuntimeState() {
213 if c.collectionLock == nil {
214 c.collectionLock = &sync.RWMutex{}
215 }
216 if c.store == nil {
217 c.store = metrix.NewCollectorStore()
218 }
219 if c.mx == nil {
220 c.mx = newCollectorMetrics(c.store)
221 }
222 }
223
224 func (c *Collector) resetRuntimeStateForInit() {
225 c.collectionLock.Lock()
226 defer c.collectionLock.Unlock()
227
228 c.discoverer = nil
229 c.scraper = nil
230 c.dsPropertyCollector = nil
231 c.clusterPropertyCollector = nil
232 c.rpPropertyCollector = nil
233 c.resources = nil
234 c.datastoreClusterMatcher = nil
235 c.vsanClusterMatcher = nil
236 c.vsanHostMatcher = nil
237 c.vsanVMMatcher = nil
238 c.vsphereTagCategoryMatcher = nil
239 c.customAttributeMatcher = nil
240 c.hostPowerPerfSamples = nil
241 c.vmPowerPerfSamples = nil
242 c.vsanMetrics = nil
243 }
244
245 func (c *Collector) closeClient() {
246 if c.vsClient == nil {
247 return
248 }
249 if err := c.vsClient.Close(); err != nil {
250 c.Warningf("close vSphere client during collector cleanup: %v", err)
251 }
252 c.vsClient = nil
253 }
254
255 func (c *Collector) stopDiscoveryTask(wait bool) {
256 if c.discoveryTask == nil {
257 return
258 }
259 c.discoveryTask.stop()
260 if wait {
261 c.discoveryTask.wait()
262 }
263 }