From aa414025d3eedd38520de749ab3b22b8a75fe968 Mon Sep 17 00:00:00 2001 From: Tesifonte Belda Date: Wed, 31 Aug 2022 12:16:36 +0200 Subject: [PATCH] [feature]: create and use govplus and tgplus packages with govmomi and telegraf helpers --- internal/vccollector/cache.go | 1 + internal/vccollector/cluster.go | 14 +-- internal/vccollector/dc.go | 4 +- internal/vccollector/host.go | 30 +++--- internal/vccollector/net.go | 10 +- internal/vccollector/storage.go | 6 +- internal/vccollector/vc.go | 4 +- internal/vccollector/vccollector.go | 107 +++--------------- internal/vccollector/vm.go | 45 ++++---- pkg/govplus/client.go | 161 ++++++++++++++++++++++++++++ pkg/govplus/errors.go | 36 +++++++ pkg/tgplus/tgplus.go | 37 +++++++ plugins/inputs/vcstat/vcstat.go | 47 +++----- 13 files changed, 329 insertions(+), 173 deletions(-) create mode 100644 pkg/govplus/client.go create mode 100644 pkg/govplus/errors.go create mode 100644 pkg/tgplus/tgplus.go diff --git a/internal/vccollector/cache.go b/internal/vccollector/cache.go index 20791b1..7f87158 100644 --- a/internal/vccollector/cache.go +++ b/internal/vccollector/cache.go @@ -18,6 +18,7 @@ import ( const ( strAsterisk = "*" ) + var ( findNotFoundError *find.NotFoundError ) diff --git a/internal/vccollector/cluster.go b/internal/vccollector/cluster.go index dcdfc20..e5a2c52 100644 --- a/internal/vccollector/cluster.go +++ b/internal/vccollector/cluster.go @@ -12,6 +12,8 @@ import ( "github.com/influxdata/telegraf" + "github.com/tesibelda/vcstat/pkg/govplus" + "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" @@ -25,14 +27,14 @@ func (c *VcCollector) CollectClusterInfo( var ( clusters []*object.ClusterComputeResource clMo mo.ClusterComputeResource - resourceSum *(types.ComputeResourceSummary) - usageSum *types.ClusterUsageSummary + resourceSum *(types.ClusterComputeResourceSummary) + usageSum *(types.ClusterUsageSummary) numVms int32 err error ) if c.client == nil { - return fmt.Errorf("Could not get clusters info: %w", Error_NoClient) + return fmt.Errorf("Could not get clusters info: %w", govplus.ErrorNoClient) } if err = c.getAllDatacentersClustersAndHosts(ctx); err != nil { return fmt.Errorf("Could not get cluster and host entity list: %w", err) @@ -49,13 +51,13 @@ func (c *VcCollector) CollectClusterInfo( err, ) } - if resourceSum = clMo.Summary.GetComputeResourceSummary(); resourceSum == nil { + if resourceSum = clMo.Summary.(*types.ClusterComputeResourceSummary); resourceSum == nil { return fmt.Errorf("Could not get cluster resource summary") } // get number of VMs in the cluster (tip: https://github.com/vmware/govmomi/issues/1247) numVms = 0 - usageSum = clMo.Summary.(*types.ClusterComputeResourceSummary).UsageSummary + usageSum = resourceSum.UsageSummary if usageSum != nil { numVms = usageSum.TotalVmCount } @@ -101,7 +103,7 @@ func getClusterFields( numhosts, numeffectivehosts int32, numcpucores, numcputhreads int16, totalcpu, totalmemory, effectivecpu, effectivememory int64, - numvms int32, + numvms int32, ) map[string]interface{} { return map[string]interface{}{ "effective_cpu": effectivecpu, diff --git a/internal/vccollector/dc.go b/internal/vccollector/dc.go index d484dc8..66510c6 100644 --- a/internal/vccollector/dc.go +++ b/internal/vccollector/dc.go @@ -11,6 +11,8 @@ import ( "time" "github.com/influxdata/telegraf" + + "github.com/tesibelda/vcstat/pkg/govplus" ) // CollectDatacenterInfo gathers datacenter info @@ -21,7 +23,7 @@ func (c *VcCollector) CollectDatacenterInfo( var err error if c.client == nil { - return fmt.Errorf("Could not get datacenters info: %w", Error_NoClient) + return fmt.Errorf("Could not get datacenters info: %w", govplus.ErrorNoClient) } if err = c.getAllDatacentersEntities(ctx); err != nil { diff --git a/internal/vccollector/host.go b/internal/vccollector/host.go index e1b6323..c0f377e 100644 --- a/internal/vccollector/host.go +++ b/internal/vccollector/host.go @@ -14,6 +14,8 @@ import ( "github.com/influxdata/telegraf" + "github.com/tesibelda/vcstat/pkg/govplus" + "github.com/vmware/govmomi/govc/host/esxcli" "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/vim25/mo" @@ -29,11 +31,15 @@ func (c *VcCollector) CollectHostInfo( hsMo mo.HostSystem hostSt *hostState err error + exit bool + s *(types.HostListSummary) + r *(types.HostRuntimeInfo) + h *(types.HostHardwareSummary) hsCode, hsConnectionCode int16 ) if c.client == nil { - return fmt.Errorf("Could not get host info: %w", Error_NoClient) + return fmt.Errorf("Could not get host info: %w", govplus.ErrorNoClient) } if err = c.getAllDatacentersClustersAndHosts(ctx); err != nil { return fmt.Errorf("Could not get cluster and host entity list: %w", err) @@ -47,7 +53,7 @@ func (c *VcCollector) CollectHostInfo( } err = host.Properties(ctx, host.Reference(), []string{"summary"}, &hsMo) if err != nil { - if err, exit := govQueryError(err); exit { + if err, exit = govplus.IsHardQueryError(err); exit { return fmt.Errorf( "Could not get host %s summary property: %w", host.Name(), @@ -63,9 +69,9 @@ func (c *VcCollector) CollectHostInfo( ) continue } - s := hsMo.Summary - r := s.Runtime - h := s.Hardware + s = &hsMo.Summary + r = s.Runtime + h = s.Hardware hostSt.setNotConnected( r.ConnectionState != types.HostSystemConnectionStateConnected, ) @@ -111,7 +117,7 @@ func (c *VcCollector) CollectHostHBA( ) if c.client == nil { - return fmt.Errorf("Could not get host HBAs info: %w", Error_NoClient) + return fmt.Errorf("Could not get host HBAs info: %w", govplus.ErrorNoClient) } if err = c.getAllDatacentersClustersAndHosts(ctx); err != nil { return fmt.Errorf("Could not get cluster and host entity list: %w", err) @@ -140,7 +146,7 @@ func (c *VcCollector) CollectHostHBA( res, err = x.Run([]string{"storage", "core", "adapter", "list"}) hostSt.setMeanResponseTime(time.Since(startTime)) if err != nil { - if err, exit := govQueryError(err); exit { + if err, exit := govplus.IsHardQueryError(err); exit { return err } acc.AddError( @@ -197,7 +203,7 @@ func (c *VcCollector) CollectHostNIC( ) if c.client == nil { - return fmt.Errorf("Could not get host NICs info: %w", Error_NoClient) + return fmt.Errorf("Could not get host NICs info: %w", govplus.ErrorNoClient) } if err = c.getAllDatacentersClustersAndHosts(ctx); err != nil { return fmt.Errorf("Could not get cluster and host entity list: %w", err) @@ -220,7 +226,7 @@ func (c *VcCollector) CollectHostNIC( res, err = x.Run([]string{"network", "nic", "list"}) hostSt.setMeanResponseTime(time.Since(startTime)) if err != nil { - if err, exit := govQueryError(err); exit { + if err, exit := govplus.IsHardQueryError(err); exit { return err } acc.AddError( @@ -279,7 +285,7 @@ func (c *VcCollector) CollectHostFw( ) if c.client == nil { - return fmt.Errorf("Could not get host firewalls info: %w", Error_NoClient) + return fmt.Errorf("Could not get host firewalls info: %w", govplus.ErrorNoClient) } if err = c.getAllDatacentersClustersAndHosts(ctx); err != nil { return fmt.Errorf("Could not get cluster and host entity list: %w", err) @@ -308,7 +314,7 @@ func (c *VcCollector) CollectHostFw( res, err = x.Run([]string{"network", "firewall", "get"}) hostSt.setMeanResponseTime(time.Since(startTime)) if err != nil { - if err, exit := govQueryError(err); exit { + if err, exit := govplus.IsHardQueryError(err); exit { return err } acc.AddError( @@ -375,7 +381,7 @@ func (c *VcCollector) ReportHostEsxcliResponse( ) if c.client == nil { - return fmt.Errorf("Could not report host esxcli responses info: %w", Error_NoClient) + return fmt.Errorf("Could not report host esxcli responses info: %w", govplus.ErrorNoClient) } for i, dc := range c.dcs { diff --git a/internal/vccollector/net.go b/internal/vccollector/net.go index 68f5bc6..f2f6c78 100644 --- a/internal/vccollector/net.go +++ b/internal/vccollector/net.go @@ -14,6 +14,8 @@ import ( "github.com/influxdata/telegraf" + "github.com/tesibelda/vcstat/pkg/govplus" + "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" @@ -34,7 +36,7 @@ func (c *VcCollector) CollectNetDVS( ) if c.client == nil { - return fmt.Errorf("Could not get network DVSs info: %w", Error_NoClient) + return fmt.Errorf("Could not get network DVSs info: %w", govplus.ErrorNoClient) } if err = c.getAllDatacentersNetworks(ctx); err != nil { return fmt.Errorf("Could not get network entity list: %w", err) @@ -55,7 +57,7 @@ func (c *VcCollector) CollectNetDVS( &dvsMo, ) if err != nil { - if err, exit := govQueryError(err); exit { + if err, exit := govplus.IsHardQueryError(err); exit { return err } acc.AddError(fmt.Errorf("Could not get dvs config property: %w", err)) @@ -102,7 +104,7 @@ func (c *VcCollector) CollectNetDVP( ) if c.client == nil { - return fmt.Errorf("Could not get network DVPs info: %w", Error_NoClient) + return fmt.Errorf("Could not get network DVPs info: %w", govplus.ErrorNoClient) } if err = c.getAllDatacentersNetworks(ctx); err != nil { return fmt.Errorf("Could not get network entity list: %w", err) @@ -122,7 +124,7 @@ func (c *VcCollector) CollectNetDVP( &dvpMo, ) if err != nil { - if err, exit := govQueryError(err); exit { + if err, exit := govplus.IsHardQueryError(err); exit { return err } acc.AddError(fmt.Errorf("Could not get dvp config property: %w", err)) diff --git a/internal/vccollector/storage.go b/internal/vccollector/storage.go index da814b3..a004df3 100644 --- a/internal/vccollector/storage.go +++ b/internal/vccollector/storage.go @@ -12,6 +12,8 @@ import ( "github.com/influxdata/telegraf" + "github.com/tesibelda/vcstat/pkg/govplus" + "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" @@ -30,7 +32,7 @@ func (c *VcCollector) CollectDatastoresInfo( ) if c.client == nil { - return fmt.Errorf("Could not get datastores info: %w", Error_NoClient) + return fmt.Errorf("Could not get datastores info: %w", govplus.ErrorNoClient) } if err = c.getAllDatacentersDatastores(ctx); err != nil { return fmt.Errorf("Could not get datastore entity list: %w", err) @@ -45,7 +47,7 @@ func (c *VcCollector) CollectDatastoresInfo( } err = pc.Retrieve(ctx, refs, []string{"summary"}, &dsMo) if err != nil { - if err, exit := govQueryError(err); exit { + if err, exit := govplus.IsHardQueryError(err); exit { return err } acc.AddError(fmt.Errorf("Could not retrieve summary for datastore: %w", err)) diff --git a/internal/vccollector/vc.go b/internal/vccollector/vc.go index dae3bbf..b178523 100644 --- a/internal/vccollector/vc.go +++ b/internal/vccollector/vc.go @@ -11,6 +11,8 @@ import ( "time" "github.com/influxdata/telegraf" + + "github.com/tesibelda/vcstat/pkg/govplus" ) // CollectVcenterInfo gathers basic vcenter info @@ -19,7 +21,7 @@ func (c *VcCollector) CollectVcenterInfo( acc telegraf.Accumulator, ) error { if c.client == nil { - return fmt.Errorf("Could not get vcenter info: %w", Error_NoClient) + return fmt.Errorf("Could not get vcenter info: %w", govplus.ErrorNoClient) } cli := c.client.Client diff --git a/internal/vccollector/vccollector.go b/internal/vccollector/vccollector.go index b3a4ca3..c00f3b5 100644 --- a/internal/vccollector/vccollector.go +++ b/internal/vccollector/vccollector.go @@ -1,8 +1,8 @@ -// vccollector package allows you to gather basic stats from VMware vCenter using govmomi library +// vccollector package allows you to gather basic stats from VMware vCenter using govmomi // -// Use NewVCCollector method to create a new struct, Open to open a session with a vCenter then -// use Collect* methods to get metrics added to a telegraf accumulator and finally Close when -// finished. +// Use NewVCCollector method to create a new struct, Open to open a session with a vCenter +// then use Collect* methods to get metrics added to a telegraf accumulator and finally +// Close when finished. // // Author: Tesifonte Belda // License: The MIT License (MIT) @@ -11,19 +11,14 @@ package vccollector import ( "context" - "errors" - "fmt" - "net" "net/url" "time" "github.com/influxdata/telegraf/plugins/common/tls" + "github.com/tesibelda/vcstat/pkg/govplus" + "github.com/vmware/govmomi" - "github.com/vmware/govmomi/session" - "github.com/vmware/govmomi/vim25" - "github.com/vmware/govmomi/vim25/methods" - "github.com/vmware/govmomi/vim25/soap" "github.com/vmware/govmomi/vim25/types" ) @@ -38,12 +33,6 @@ type VcCollector struct { VcCache } -// Common errors raised by vccollector -var ( - Error_NoClient = errors.New("no vCenter client, no session has been opened") - Error_NotVC = errors.New("endpoint does not look like a vCenter") -) - // NewVCCollector returns a new VcCollector associated with the provided vCenter URL func NewVCCollector( ctx context.Context, @@ -60,14 +49,10 @@ func NewVCCollector( vcc.TLSCA = clicfg.TLSCA vcc.InsecureSkipVerify = clicfg.InsecureSkipVerify - // Parse URL params - if vcc.url, err = soap.ParseURL(vcenterUrl); err != nil { - return nil, fmt.Errorf("Error parsing URL for vcenter: %w", err) - } - if vcc.url == nil { - return nil, fmt.Errorf("Error parsing URL for vcenter: returned nil") + vcc.url, err = govplus.PaseURL(vcenterUrl, user, pass) + if err != nil { + return nil, err } - vcc.url.User = url.UserPassword(user, pass) return &vcc, err } @@ -93,78 +78,29 @@ func (c *VcCollector) Open(ctx context.Context, timeout time.Duration) error { defer cancel1() if c.client != nil { // Try to relogin and if not possible reopen session - if err = c.client.Login(ctx1, c.url.User); err != nil { - c.Close(ctx) - if err = c.Open(ctx, timeout); err != nil { - return err - } - } - } else { - var cli *govmomi.Client - - // Create a vSphere vCenter client using CA if provided - if c.TLSCA == "" { - cli, err = govmomi.NewClient(ctx1, c.url, c.InsecureSkipVerify) - } else { - cli, err = c.newCAClient(ctx1) - } - if err != nil { - return err + if err = c.client.Login(ctx1, c.url.User); err == nil { + return nil } - - c.client = cli - } - if !c.client.IsVC() { c.Close(ctx) - return fmt.Errorf("Could not open vCenter session: %w", Error_NotVC) } + c.client, err = govplus.NewClient(ctx1, c.url, &c.ClientConfig) return err } // IsActive returns if the vCenter connection is active or not func (c *VcCollector) IsActive(ctx context.Context) bool { - if c == nil || c.client == nil || !c.client.Valid() { - return false - } - - ctx1, cancel1 := context.WithTimeout(ctx, time.Duration(5 * time.Second)) - defer cancel1() - _, err := methods.GetCurrentTime(ctx1, c.client) //nolint no need current time - - return err == nil + return govplus.ClientIsActive(ctx, c.client) } // Close closes vCenter connection func (c *VcCollector) Close(ctx context.Context) { if c.client != nil { - c.client.Logout(ctx) //nolint //no need for logout error checking + govplus.CloseClient(ctx, c.client) c.client = nil } } -// newCAClient creates a Client but logins after setting CA, not before -func (c *VcCollector) newCAClient(ctx context.Context) (*govmomi.Client, error) { - var err error - - soapClient := soap.NewClient(c.url, c.InsecureSkipVerify) - if err = soapClient.SetRootCAs(c.TLSCA); err != nil { - return nil, err - } - vimClient, err := vim25.NewClient(ctx, soapClient) - if err != nil { - return nil, err - } - - cli := &govmomi.Client{ - Client: vimClient, - SessionManager: session.NewManager(vimClient), - } - err = cli.Login(ctx, c.url.User) - - return cli, err -} - // entityStatusCode converts types.ManagedEntityStatus to int16 for easy alerting func entityStatusCode(status types.ManagedEntityStatus) int16 { switch status { @@ -180,18 +116,3 @@ func entityStatusCode(status types.ManagedEntityStatus) int16 { return 1 } } - -// govQueryError returns false if error is light and we may continue quering -func govQueryError(err error) (error, bool) { - if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { - return err, true - } - - var dnsError *net.DNSError - var opError *net.OpError - if errors.As(err, &dnsError) || errors.As(err, &opError) { - return err, true - } - - return err, false -} diff --git a/internal/vccollector/vm.go b/internal/vccollector/vm.go index 7f1c798..00fe471 100644 --- a/internal/vccollector/vm.go +++ b/internal/vccollector/vm.go @@ -12,8 +12,11 @@ import ( "github.com/influxdata/telegraf" + "github.com/tesibelda/vcstat/pkg/govplus" + "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" ) // CollectVmsInfo gathers basic virtual machine info @@ -22,14 +25,18 @@ func (c *VcCollector) CollectVmsInfo( acc telegraf.Accumulator, ) error { var ( - vmMo mo.VirtualMachine - err error - host *object.HostSystem + vmMo mo.VirtualMachine + err error + exit bool + host *object.HostSystem + s *types.VirtualMachineSummary + r *types.VirtualMachineRuntimeInfo + t *types.VirtualMachineConfigSummary hostname, clustername string ) if c.client == nil { - return fmt.Errorf("Could not get VMs info: %w", Error_NoClient) + return fmt.Errorf("Could not get VMs info: %w", govplus.ErrorNoClient) } if err := c.getAllDatacentersVMs(ctx); err != nil { @@ -40,7 +47,7 @@ func (c *VcCollector) CollectVmsInfo( for _, vm := range c.vms[i] { err = vm.Properties(ctx, vm.Reference(), []string{"summary"}, &vmMo) if err != nil { - if err, exit := govQueryError(err); exit { + if err, exit = govplus.IsHardQueryError(err); exit { return fmt.Errorf( "Could not get vm %s summary property: %w", vm.Name(), @@ -56,9 +63,9 @@ func (c *VcCollector) CollectVmsInfo( ) continue } - s := vmMo.Summary - r := s.Runtime - t := s.Config + s = &vmMo.Summary + r = &s.Runtime + t = &s.Config hostname = "" clustername = "" if host = c.getHostObjectFromReference(i, r.Host); host != nil { @@ -128,17 +135,17 @@ func getVmFields( "connection_state": connectionstate, "connection_state_code": connectioncode, "consolidation_needed": consolidationneeded, - "max_cpu_usage": maxcpu, - "max_mem_usage": maxmemory, - "memory_size": memorysize, - "num_eth_cards": numeth, - "num_vdisks": numvdisk, - "num_vcpus": numcpu, - "power_state": powerstate, - "power_state_code": powerstatecode, - "status": overallstatus, - "status_code": vmstatuscode, - "template": template, + "max_cpu_usage": maxcpu, + "max_mem_usage": maxmemory, + "memory_size": memorysize, + "num_eth_cards": numeth, + "num_vdisks": numvdisk, + "num_vcpus": numcpu, + "power_state": powerstate, + "power_state_code": powerstatecode, + "status": overallstatus, + "status_code": vmstatuscode, + "template": template, } } diff --git a/pkg/govplus/client.go b/pkg/govplus/client.go new file mode 100644 index 0000000..eedd3b0 --- /dev/null +++ b/pkg/govplus/client.go @@ -0,0 +1,161 @@ +// govplus is a basic govmomi helper library for using vSphere API +// This file contains API client related functions and definitions +// +// Author: Tesifonte Belda +// License: The MIT License (MIT) + +package govplus + +import ( + "context" + "fmt" + "net/url" + "time" + + "github.com/influxdata/telegraf/plugins/common/tls" + + "github.com/vmware/govmomi" + "github.com/vmware/govmomi/session" + "github.com/vmware/govmomi/session/cache" + "github.com/vmware/govmomi/vapi/rest" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/soap" +) + +// NewClient creates a vSphere vim25.Client (ie inventory queries) +func NewClient( + ctx context.Context, + u *url.URL, + t *tls.ClientConfig, +) (*govmomi.Client, error) { + var ( + c *govmomi.Client + err error + ) + + if u == nil || u.User == nil { + return nil, ErrorURLNil + } + + if t.TLSCA == "" { + c, err = govmomi.NewClient(ctx, u, t.InsecureSkipVerify) + } else { + c, err = newCAClient(ctx, u, t) + } + if err != nil { + return nil, err + } + + if !c.Client.IsVC() { + return nil, ErrorNotVC + } + + return c, nil +} + +// NewRestClient creates a vSphere rest.Client (ie tags queries) +func NewRestClient( + ctx context.Context, + u *url.URL, + t *tls.ClientConfig, + c *govmomi.Client, +) (*rest.Client, error) { + if c == nil || c.Client == nil { + return nil, ErrorNoClient + } + // Share govc's session cache + s := &cache.Session{ + URL: u, + Insecure: t.InsecureSkipVerify, + } + + rc := rest.NewClient(c.Client) + err := s.Login(ctx, rc, nil) + if err != nil { + return nil, err + } + + return rc, nil +} + +// CloseClient closes govmomi client +func CloseClient(ctx context.Context, c *govmomi.Client) { + if c != nil { + _ = c.Logout(ctx) //nolint: no worries for logout errors + } +} + +// CloseRestClient closes vSphere rest client +func CloseRestClient(ctx context.Context, rc *rest.Client) { + if rc != nil { + _ = rc.Logout(ctx) //nolint: no worries for logout errors + } +} + +// PaseURL parses vcenter URL params +func PaseURL(vcenterUrl, user, pass string) (*url.URL, error) { + u, err := soap.ParseURL(vcenterUrl) + if err != nil { + return nil, fmt.Errorf("%s: %w", ErrorURLParsing.Error(), err) + } + if u == nil { + return nil, fmt.Errorf("%w: returned nil", ErrorURLParsing) + } + u.User = url.UserPassword(user, pass) + + return u, nil +} + +// ClientIsActive returns true if the vCenter soap session is active +func ClientIsActive(ctx context.Context, c *govmomi.Client) bool { + if c == nil || !c.Client.Valid() { + return false + } + + ctx1, cancel1 := context.WithTimeout(ctx, time.Duration(5*time.Second)) + defer cancel1() + _, err := methods.GetCurrentTime(ctx1, c.Client) //nolint no need current time + + return err == nil +} + +// RestClientIsActive returns true if the vCenter rest session is active +func RestClientIsActive(ctx context.Context, rc *rest.Client) bool { + if rc == nil { + return false + } + + s, err := rc.Session(ctx) + if err != nil || s == nil { + return false + } + + return true +} + +// newCAClient creates a Client but logins after setting CA, not before +func newCAClient( + ctx context.Context, + u *url.URL, + t *tls.ClientConfig, +) (*govmomi.Client, error) { + var err error + + soapClient := soap.NewClient(u, t.InsecureSkipVerify) + if err = soapClient.SetRootCAs(t.TLSCA); err != nil { + return nil, err + } + vimClient, err := vim25.NewClient(ctx, soapClient) + if err != nil { + return nil, err + } + + cli := &govmomi.Client{ + Client: vimClient, + SessionManager: session.NewManager(vimClient), + } + err = cli.Login(ctx, u.User) + + return cli, err +} diff --git a/pkg/govplus/errors.go b/pkg/govplus/errors.go new file mode 100644 index 0000000..164a6f1 --- /dev/null +++ b/pkg/govplus/errors.go @@ -0,0 +1,36 @@ +// govplus is a basic govmomi helper library for using vSphere API +// This file contains error related functions and definitions +// +// Author: Tesifonte Belda +// License: The MIT License (MIT) + +package govplus + +import ( + "context" + "errors" + "net" +) + +// Common raised errors +var ( + ErrorNoClient = errors.New("no vCenter client, no session has been opened") + ErrorNotVC = errors.New("endpoint does not look like a vCenter") + ErrorURLParsing = errors.New("error parsing URL for vcenter") + ErrorURLNil = errors.New("vcenter URL should not be nil") +) + +// IsHardQueryError returns false if error is light and we may continue quering +func IsHardQueryError(err error) (error, bool) { + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + return err, true + } + + var dnsError *net.DNSError + var opError *net.OpError + if errors.As(err, &dnsError) || errors.As(err, &opError) { + return err, true + } + + return err, false +} diff --git a/pkg/tgplus/tgplus.go b/pkg/tgplus/tgplus.go new file mode 100644 index 0000000..3ec404c --- /dev/null +++ b/pkg/tgplus/tgplus.go @@ -0,0 +1,37 @@ +// tgplus is a basic telegraf helper library +// +// Author: Tesifonte Belda +// License: The MIT License (MIT) + +package tgplus + +import ( + "context" + "time" + + "github.com/influxdata/telegraf" +) + +// GatherError adds the error to the telegraf accumulator +func GatherError(acc telegraf.Accumulator, err error) error { + // No need to signal errors if we were merely canceled. + if err == context.Canceled { + return nil + } + acc.AddError(err) + return nil +} + +// GetPrecision returns the rounding precision for metrics +func GetPrecision(interval time.Duration) time.Duration { + switch { + case interval >= time.Second: + return time.Second + case interval >= time.Millisecond: + return time.Millisecond + case interval >= time.Microsecond: + return time.Microsecond + default: + return time.Nanosecond + } +} \ No newline at end of file diff --git a/plugins/inputs/vcstat/vcstat.go b/plugins/inputs/vcstat/vcstat.go index fc765a3..10f8a8e 100644 --- a/plugins/inputs/vcstat/vcstat.go +++ b/plugins/inputs/vcstat/vcstat.go @@ -19,6 +19,7 @@ import ( "github.com/influxdata/telegraf/selfstat" "github.com/tesibelda/vcstat/internal/vccollector" + "github.com/tesibelda/vcstat/pkg/tgplus" ) type VCstatConfig struct { @@ -189,9 +190,9 @@ func (vcs *VCstatConfig) Gather(acc telegraf.Accumulator) error { ) if err = vcs.keepActiveSession(acc); err != nil { - return gatherError(acc, err) + return tgplus.GatherError(acc, err) } - acc.SetPrecision(getPrecision(vcs.pollInterval)) + acc.SetPrecision(tgplus.GetPrecision(vcs.pollInterval)) // poll using a context with timeout ctxT, cancelT := context.WithTimeout(vcs.ctx, time.Duration(vcs.Timeout)) @@ -200,23 +201,23 @@ func (vcs *VCstatConfig) Gather(acc telegraf.Accumulator) error { //--- Get vCenter, DCs and Clusters info if err = vcs.gatherHighLevelEntities(ctxT, acc); err != nil { - return gatherError(acc, err) + return tgplus.GatherError(acc, err) } //--- Get Hosts, Networks and Storage info if err = vcs.gatherHost(ctxT, acc); err != nil { - return gatherError(acc, err) + return tgplus.GatherError(acc, err) } if err = vcs.gatherNetwork(ctxT, acc); err != nil { - return gatherError(acc, err) + return tgplus.GatherError(acc, err) } if err = vcs.gatherStorage(ctxT, acc); err != nil { - return gatherError(acc, err) + return tgplus.GatherError(acc, err) } //--- Get VM info if err = vcs.gatherVM(ctxT, acc); err != nil { - return gatherError(acc, err) + return tgplus.GatherError(acc, err) } // selfmonitoring @@ -357,12 +358,12 @@ func (vcs *VCstatConfig) gatherStorage(ctx context.Context, acc telegraf.Accumul if vcs.DatastoreInstances { var col *vccollector.VcCollector var err error - col = vcs.vcc + col = vcs.vcc if col == nil { return nil } if err = col.CollectDatastoresInfo(ctx, acc); err != nil { - return gatherError(acc, err) + return tgplus.GatherError(acc, err) } } @@ -374,38 +375,14 @@ func (vcs *VCstatConfig) gatherVM(ctx context.Context, acc telegraf.Accumulator) if vcs.VMInstances { var col *vccollector.VcCollector var err error - col = vcs.vcc + col = vcs.vcc if col == nil { return nil } if err = col.CollectVmsInfo(ctx, acc); err != nil { - return gatherError(acc, err) + return tgplus.GatherError(acc, err) } } return nil } - -// gatherError adds the error to the telegraf accumulator -func gatherError(acc telegraf.Accumulator, err error) error { - // No need to signal errors if we were merely canceled. - if err == context.Canceled { - return nil - } - acc.AddError(err) - return nil -} - -// getPrecision returns the rounding precision for metrics -func getPrecision(interval time.Duration) time.Duration { - switch { - case interval >= time.Second: - return time.Second - case interval >= time.Millisecond: - return time.Millisecond - case interval >= time.Microsecond: - return time.Microsecond - default: - return time.Nanosecond - } -}