master
go 316 lines 10.1 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package discover
4
5 import (
6 "fmt"
7 "strings"
8 "time"
9
10 "github.com/netdata/netdata/go/plugins/logger"
11 "github.com/netdata/netdata/go/plugins/pkg/matcher"
12 "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/vsphere/match"
13 rs "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/vsphere/resources"
14
15 "github.com/vmware/govmomi/vim25/mo"
16 "github.com/vmware/govmomi/vim25/types"
17 )
18
19 const (
20 logKeyNetworkTopologyDiscoveryError = "vsphere-discover-network-topology-error"
21 logKeyDatastoreClusterDiscoveryError = "vsphere-discover-datastore-cluster-error"
22 logKeyCustomAttributeDiscoveryError = "vsphere-discover-custom-attribute-error"
23 logKeyTagDiscoveryError = "vsphere-discover-tag-error"
24 )
25
26 type Client interface {
27 Datacenters(pathSet ...string) ([]mo.Datacenter, error)
28 Folders(pathSet ...string) ([]mo.Folder, error)
29 ComputeResources(pathSet ...string) ([]mo.ComputeResource, error)
30 Hosts(pathSet ...string) ([]mo.HostSystem, error)
31 VirtualMachines(pathSet ...string) ([]mo.VirtualMachine, error)
32 Datastores(pathSet ...string) ([]mo.Datastore, error)
33 Networks(pathSet ...string) ([]mo.Network, error)
34 StoragePods(pathSet ...string) ([]mo.StoragePod, error)
35 ResourcePools(pathSet ...string) ([]mo.ResourcePool, error)
36
37 CustomFields() ([]types.CustomFieldDef, error)
38 TagsByRef(refs []types.ManagedObjectReference) (map[types.ManagedObjectReference]map[string][]string, error)
39 CounterInfoByName() (map[string]*types.PerfCounterInfo, error)
40 }
41
42 func New(client Client) *Discoverer {
43 return &Discoverer{
44 Client: client,
45 missingPerfCounterWarnings: make(map[string]bool),
46 }
47 }
48
49 type Discoverer struct {
50 *logger.Logger
51 Client
52 match.HostMatcher
53 match.VMMatcher
54 match.DatastoreMatcher
55 match.ClusterMatcher
56 match.DatastoreClusterMatcher
57 CollectDatastoreClusters bool
58 CollectVSAN bool
59 CollectNetworkTopology bool
60 TagCategoryMatcher matcher.Matcher
61 CustomAttributeMatcher matcher.Matcher
62 missingPerfCounterWarnings map[string]bool
63 }
64
65 type resources struct {
66 dcs []mo.Datacenter
67 folders []mo.Folder
68 clusters []mo.ComputeResource
69 hosts []mo.HostSystem
70 vms []mo.VirtualMachine
71 datastores []mo.Datastore
72 networks []mo.Network
73 storagePods []mo.StoragePod
74 resourcePools []mo.ResourcePool
75 }
76
77 func (d *Discoverer) Discover() (*rs.Resources, error) {
78 startTime := time.Now()
79 raw, err := d.discover()
80 if err != nil {
81 return nil, fmt.Errorf("discover vSphere inventory resources: %w", err)
82 }
83
84 res := d.build(raw)
85
86 err = d.setHierarchy(res)
87 if err != nil {
88 // TODO: handle objects w/o hier?
89 d.Error(err)
90 }
91
92 numC := len(res.Clusters)
93 numH := len(res.Hosts)
94 numV := len(res.VMs)
95 numD := len(res.Datastores)
96 numSP := len(res.StoragePods)
97 d.removeUnmatched(res)
98 if len(res.Clusters)+len(res.Hosts)+len(res.VMs)+len(res.Datastores)+len(res.StoragePods) == 0 {
99 return nil, fmt.Errorf("all resources were filtered (%d clusters, %d hosts, %d vms, %d datastores, %d datastore clusters)", numC, numH, numV, numD, numSP)
100 }
101
102 d.collectEnrichmentLabels(res)
103
104 err = d.collectMetricLists(res)
105 if err != nil {
106 return nil, fmt.Errorf("collect vSphere performance metric lists: %w", err)
107 }
108
109 d.Infof("discovering : discovered %d/%d clusters, %d/%d hosts, %d/%d vms, %d/%d datastores, %d/%d datastore clusters, %d resource pools, the whole process took %s",
110 len(res.Clusters),
111 numOfRealClusters(raw.clusters),
112 len(res.Hosts),
113 len(raw.hosts),
114 len(res.VMs),
115 len(raw.vms),
116 len(res.Datastores),
117 len(raw.datastores),
118 len(res.StoragePods),
119 len(raw.storagePods),
120 len(res.ResourcePools),
121 time.Since(startTime))
122
123 return res, nil
124 }
125
126 var (
127 // properties to set
128 datacenterPathSet = []string{"name", "parent"}
129 folderPathSet = []string{"name", "parent"}
130 clusterPathSetBase = []string{"name", "parent"}
131 hostPathSetBase = []string{"name", "parent", "runtime.connectionState", "runtime.powerState", "runtime.inMaintenanceMode", "summary.overallStatus"}
132 vmPathSetBase = []string{"name", "parent", "runtime.host", "runtime.connectionState", "runtime.powerState", "runtime.consolidationNeeded", "summary.guest", "summary.config", "summary.storage", "summary.overallStatus", "snapshot"}
133 datastorePathSetBase = []string{"name", "parent", "summary", "overallStatus"}
134 networkPathSetBase = []string{"name", "parent", "summary", "host", "vm", "overallStatus"}
135 storagePodPathSetBase = []string{"name", "parent", "summary", "overallStatus", "podStorageDrsEntry.storageDrsConfig.podConfig.enabled"}
136 resourcePoolPathSetBase = []string{"name", "owner"}
137 )
138
139 func (d Discoverer) clusterPathSet() []string {
140 pathSet := append([]string(nil), clusterPathSetBase...)
141 if d.CollectVSAN {
142 pathSet = append(pathSet, "configurationEx.vsanConfigInfo")
143 }
144 return d.withCustomValues(pathSet)
145 }
146
147 func (d Discoverer) hostPathSet() []string {
148 pathSet := append([]string(nil), hostPathSetBase...)
149 if d.CollectVSAN {
150 pathSet = append(pathSet, "config.vsanHostConfig.clusterInfo.nodeUuid")
151 }
152 return d.withCustomValues(pathSet)
153 }
154
155 func (d Discoverer) vmPathSet() []string {
156 pathSet := append([]string(nil), vmPathSetBase...)
157 if d.CollectVSAN {
158 pathSet = append(pathSet, "config.instanceUuid")
159 }
160 return d.withCustomValues(pathSet)
161 }
162
163 func (d Discoverer) datastorePathSet() []string {
164 return d.withCustomValues(datastorePathSetBase)
165 }
166
167 func (d Discoverer) networkPathSet() []string {
168 return d.withCustomValues(networkPathSetBase)
169 }
170
171 func (d Discoverer) storagePodPathSet() []string {
172 return d.withCustomValues(storagePodPathSetBase)
173 }
174
175 func (d Discoverer) resourcePoolPathSet() []string {
176 return d.withCustomValues(resourcePoolPathSetBase)
177 }
178
179 func (d Discoverer) withCustomValues(pathSet []string) []string {
180 out := append([]string(nil), pathSet...)
181 if d.CustomAttributeMatcher != nil {
182 out = append(out, "customValue")
183 }
184 return out
185 }
186
187 func (d Discoverer) discover() (*resources, error) {
188 d.Debug("discovering : starting resource discovering process")
189
190 start := time.Now()
191 t := start
192 datacenters, err := d.Datacenters(datacenterPathSet...)
193 if err != nil {
194 return nil, discoverRetrieveError("datacenters", datacenterPathSet, err)
195 }
196 d.Debugf("discovering : found %d dcs, process took %s", len(datacenters), time.Since(t))
197
198 t = time.Now()
199 folders, err := d.Folders(folderPathSet...)
200 if err != nil {
201 return nil, discoverRetrieveError("folders", folderPathSet, err)
202 }
203 d.Debugf("discovering : found %d folders, process took %s", len(folders), time.Since(t))
204
205 t = time.Now()
206 clusters, err := d.ComputeResources(d.clusterPathSet()...)
207 if err != nil {
208 return nil, discoverRetrieveError("compute resources", d.clusterPathSet(), err)
209 }
210 d.Debugf("discovering : found %d clusters, process took %s", len(clusters), time.Since(t))
211
212 t = time.Now()
213 hosts, err := d.Hosts(d.hostPathSet()...)
214 if err != nil {
215 return nil, discoverRetrieveError("hosts", d.hostPathSet(), err)
216 }
217 d.Debugf("discovering : found %d hosts, process took %s", len(hosts), time.Since(t))
218
219 t = time.Now()
220 vms, err := d.VirtualMachines(d.vmPathSet()...)
221 if err != nil {
222 return nil, discoverRetrieveError("virtual machines", d.vmPathSet(), err)
223 }
224 d.Debugf("discovering : found %d vms, process took %s", len(vms), time.Since(t))
225
226 t = time.Now()
227 datastores, err := d.Datastores(d.datastorePathSet()...)
228 if err != nil {
229 return nil, discoverRetrieveError("datastores", d.datastorePathSet(), err)
230 }
231 d.Debugf("discovering : found %d datastores, process took %s", len(datastores), time.Since(t))
232
233 var networks []mo.Network
234 if d.CollectNetworkTopology {
235 t = time.Now()
236 networks, err = d.Networks(d.networkPathSet()...)
237 if err != nil {
238 d.warnLimited(logKeyNetworkTopologyDiscoveryError, "discovering : failed to discover networks for topology: %v", discoverRetrieveError("networks", d.networkPathSet(), err))
239 } else {
240 d.Debugf("discovering : found %d networks, process took %s", len(networks), time.Since(t))
241 }
242 }
243
244 var storagePods []mo.StoragePod
245 if d.CollectDatastoreClusters {
246 t = time.Now()
247 storagePods, err = d.StoragePods(d.storagePodPathSet()...)
248 if err != nil {
249 d.warnLimited(logKeyDatastoreClusterDiscoveryError, "discovering : failed to discover datastore clusters: %v", discoverRetrieveError("datastore clusters", d.storagePodPathSet(), err))
250 } else {
251 d.Debugf("discovering : found %d datastore clusters, process took %s", len(storagePods), time.Since(t))
252 }
253 }
254
255 t = time.Now()
256 resourcePools, err := d.ResourcePools(d.resourcePoolPathSet()...)
257 if err != nil {
258 return nil, discoverRetrieveError("resource pools", d.resourcePoolPathSet(), err)
259 }
260 d.Debugf("discovering : found %d resource pools, process took %s", len(resourcePools), time.Since(t))
261
262 raw := resources{
263 dcs: datacenters,
264 folders: folders,
265 clusters: clusters,
266 hosts: hosts,
267 vms: vms,
268 datastores: datastores,
269 networks: networks,
270 storagePods: storagePods,
271 resourcePools: resourcePools,
272 }
273
274 d.Infof("discovering : found %d dcs, %d folders, %d clusters (%d dummy), %d hosts, %d vms, %d datastores, %d networks, %d datastore clusters, %d resource pools, process took %s",
275 len(raw.dcs),
276 len(raw.folders),
277 len(clusters),
278 numOfDummyClusters(clusters),
279 len(raw.hosts),
280 len(raw.vms),
281 len(raw.datastores),
282 len(raw.networks),
283 len(raw.storagePods),
284 len(raw.resourcePools),
285 time.Since(start),
286 )
287
288 return &raw, nil
289 }
290
291 func (d Discoverer) warnLimited(key, format string, args ...any) {
292 d.Limit(key, 1, time.Hour).Warningf(format, args...)
293 }
294
295 func discoverRetrieveError(resource string, pathSet []string, err error) error {
296 return fmt.Errorf("retrieve %s from vSphere inventory pathSet=[%s]: %w", resource, strings.Join(pathSet, ","), err)
297 }
298
299 func numOfDummyClusters(clusters []mo.ComputeResource) (num int) {
300 for _, c := range clusters {
301 // domain-s61 | domain-c52
302 if strings.HasPrefix(c.Reference().Value, "domain-s") {
303 num++
304 }
305 }
306 return num
307 }
308
309 func numOfRealClusters(clusters []mo.ComputeResource) int {
310 return len(clusters) - numOfDummyClusters(clusters)
311 }
312
313 // isDummyCluster returns true for standalone-host placeholder clusters (domain-s*).
314 func isDummyCluster(id string) bool {
315 return strings.HasPrefix(id, "domain-s")
316 }