diff --git a/README.md b/README.md index 46c6f23..853d88f 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ Metrics are collected from the Prism Element v2.0 APIs. Currently, the exporter - VMs - Storage Containers +Additionally, the v1 VM API is supported for gathering additional stats not available in the v2 API. The config file for this endpoint is `vm_v1.yaml`. + The response from the API contains a list of entities, each with a set of key-value pairs. The exporter will flatten these key-value pairs and expose them as Prometheus metrics. `/config` contains a YAML configuration file for each exporter. This is where the metrics to be collected are defined. Any value in the API response can be collected; however, the exporter will only collect metrics that are defined in the configuration file. It is important to note that nested fields in the API response are flattened and exposed like "parent_child", e.g. "stats_num_iops". @@ -76,7 +78,7 @@ For both methods, start by cloning the repository and navigating to the repo roo To build and run the Go binary natively: -1. Download and install Go from [here](https://go.dev/doc/install) +1. Download and install the [Go Programming Language](https://go.dev/doc/install) 2. Export all necessary environment variables 3. `go run cmd/nutanix_exporter/main.go` 4. The exporter will now be running on `localhost:9408` diff --git a/configs/vm_v1.yaml b/configs/vm_v1.yaml new file mode 100644 index 0000000..b06b881 --- /dev/null +++ b/configs/vm_v1.yaml @@ -0,0 +1,6 @@ +- name: stats_memory_usage_ppm + help: Memory usage in parts per million (PPM). +- name: stats_hypervisor_cpu_usage_ppm + help: Hypervisor CPU usage in parts per million (PPM). +- name: stats_controller_user_bytes + help: Controller user bytes. \ No newline at end of file diff --git a/internal/prom/exporter.go b/internal/prom/exporter.go index d74ac17..835e769 100644 --- a/internal/prom/exporter.go +++ b/internal/prom/exporter.go @@ -219,6 +219,9 @@ func (e *Exporter) processEntity(ent map[string]interface{}, isCluster bool) { // For entity-level metrics, use both cluster name and entity name as labels if name, ok := ent["name"].(string); ok { labelValues = []string{e.Cluster.Name, name} + // Check for vmname key if name key is not present (used in VMv1 API) + } else if name, ok := ent["vmName"].(string); ok { + labelValues = []string{e.Cluster.Name, name} } else { // Handle case where "name" is missing or not a string labelValues = []string{e.Cluster.Name, "unknown"} diff --git a/internal/prom/exporters.go b/internal/prom/exporters.go index 3638647..a7138af 100644 --- a/internal/prom/exporters.go +++ b/internal/prom/exporters.go @@ -40,6 +40,10 @@ type VmExporter struct { *Exporter } +type Vmv1Exporter struct { + *Exporter +} + type StorageContainerExporter struct { *Exporter } @@ -73,6 +77,15 @@ func NewVMCollector(cluster *nutanix.Cluster, configPath string) *VmExporter { return exporter } +func NewVMv1Collector(cluster *nutanix.Cluster, configPath string) *Vmv1Exporter { + labels := []string{"cluster_name", "vm_name"} + exporter := &Vmv1Exporter{ + Exporter: NewExporter(cluster, labels), + } + exporter.initMetrics(configPath, labels) + return exporter +} + func NewStorageContainerCollector(cluster *nutanix.Cluster, configPath string) *StorageContainerExporter { labels := []string{"cluster_name", "container_name"} exporter := &StorageContainerExporter{ @@ -155,3 +168,21 @@ func (e *VmExporter) Collect(ch chan<- prometheus.Metric) { gaugeVec.Collect(ch) } } + +// Collect +func (e *Vmv1Exporter) Collect(ch chan<- prometheus.Metric) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + result, err := e.fetchData(ctx, "/v1/vms/") + if err != nil { + slog.Error("Error fetching VM v1 data", "error", err) + return + } + + e.updateMetrics(result) + + for _, gaugeVec := range e.Metrics { + gaugeVec.Collect(ch) + } +} diff --git a/internal/service/exporter.go b/internal/service/exporter.go index ea272f0..20bbe10 100644 --- a/internal/service/exporter.go +++ b/internal/service/exporter.go @@ -173,6 +173,7 @@ func (es *ExporterService) refreshClusters() error { prom.NewClusterCollector(cluster, es.config.ConfigPath+"/cluster.yaml"), prom.NewHostCollector(cluster, es.config.ConfigPath+"/host.yaml"), prom.NewVMCollector(cluster, es.config.ConfigPath+"/vm.yaml"), + prom.NewVMv1Collector(cluster, es.config.ConfigPath+"/vm_v1.yaml"), } for _, collector := range collectors {