Skip to content

Commit

Permalink
Merge pull request kubevirt#13428 from machadovilaca/CNV-51568-51569
Browse files Browse the repository at this point in the history
Add kubevirt_vmi_migration_(start|end)_time_seconds metrics
  • Loading branch information
kubevirt-bot authored Jan 23, 2025
2 parents 48adc16 + fce50d3 commit 7b98f29
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 0 deletions.
6 changes: 6 additions & 0 deletions docs/observability/metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,18 @@ The rate of memory being dirty in the Guest OS. Type: Gauge.
### kubevirt_vmi_migration_disk_transfer_rate_bytes
The rate at which the memory is being transferred. Type: Gauge.

### kubevirt_vmi_migration_end_time_seconds
The time at which the migration ended. Type: Gauge.

### kubevirt_vmi_migration_failed
Indicates if the VMI migration failed. Type: Gauge.

### kubevirt_vmi_migration_phase_transition_time_from_creation_seconds
Histogram of VM migration phase transitions duration from creation time in seconds. Type: Histogram.

### kubevirt_vmi_migration_start_time_seconds
The time at which the migration started. Type: Gauge.

### kubevirt_vmi_migration_succeeded
Indicates if the VMI migration succeeded. Type: Gauge.

Expand Down
1 change: 1 addition & 0 deletions pkg/monitoring/metrics/virt-controller/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ go_library(
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
],
)
Expand Down
81 changes: 81 additions & 0 deletions pkg/monitoring/metrics/virt-controller/vmistats_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/machadovilaca/operator-observability/pkg/operatormetrics"
k8sv1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/cache"

"kubevirt.io/client-go/log"
Expand Down Expand Up @@ -55,6 +56,8 @@ var (
vmiInfo,
vmiEvictionBlocker,
vmiAddresses,
vmiMigrationStartTime,
vmiMigrationEndTime,
},
CollectCallback: vmiStatsCollectorCallback,
}
Expand Down Expand Up @@ -97,6 +100,22 @@ var (
},
[]string{"node", "namespace", "name", "network_name", "address", "type"},
)

vmiMigrationStartTime = operatormetrics.NewGaugeVec(
operatormetrics.MetricOpts{
Name: "kubevirt_vmi_migration_start_time_seconds",
Help: "The time at which the migration started.",
},
[]string{"node", "namespace", "name", "migration_name"},
)

vmiMigrationEndTime = operatormetrics.NewGaugeVec(
operatormetrics.MetricOpts{
Name: "kubevirt_vmi_migration_end_time_seconds",
Help: "The time at which the migration ended.",
},
[]string{"node", "namespace", "name", "migration_name", "status"},
)
)

func vmiStatsCollectorCallback() []operatormetrics.CollectorResult {
Expand All @@ -122,6 +141,7 @@ func reportVmisStats(vmis []*k6tv1.VirtualMachineInstance) []operatormetrics.Col
crs = append(crs, collectVMIInfo(vmi))
crs = append(crs, getEvictionBlocker(vmi))
crs = append(crs, collectVMIInterfacesInfo(vmi)...)
crs = append(crs, collectVMIMigrationTime(vmi)...)
}

return crs
Expand Down Expand Up @@ -330,3 +350,64 @@ func collectVMIInterfaceInfo(vmi *k6tv1.VirtualMachineInstance, iface k6tv1.Virt
Value: 1.0,
}
}

func collectVMIMigrationTime(vmi *k6tv1.VirtualMachineInstance) []operatormetrics.CollectorResult {
var cr []operatormetrics.CollectorResult
var migrationName string

if vmi.Status.MigrationState == nil {
return cr
}

migrationName = getMigrationNameFromMigrationUID(vmi.Namespace, vmi.Status.MigrationState.MigrationUID)

if vmi.Status.MigrationState.StartTimestamp != nil {
cr = append(cr, operatormetrics.CollectorResult{
Metric: vmiMigrationStartTime,
Value: float64(vmi.Status.MigrationState.StartTimestamp.Time.Unix()),
Labels: []string{vmi.Status.NodeName, vmi.Namespace, vmi.Name, migrationName},
})
}

if vmi.Status.MigrationState.EndTimestamp != nil {
cr = append(cr, operatormetrics.CollectorResult{
Metric: vmiMigrationEndTime,
Value: float64(vmi.Status.MigrationState.EndTimestamp.Time.Unix()),
Labels: []string{vmi.Status.NodeName, vmi.Namespace, vmi.Name, migrationName,
calculateMigrationStatus(vmi.Status.MigrationState),
},
})
}

return cr
}

func calculateMigrationStatus(migrationState *k6tv1.VirtualMachineInstanceMigrationState) string {
if !migrationState.Completed {
return ""
}

if migrationState.Failed {
return "failed"
}

return "succeeded"
}

func getMigrationNameFromMigrationUID(namespace string, migrationUID types.UID) string {
objs, err := vmiMigrationInformer.GetIndexer().ByIndex(cache.NamespaceIndex, namespace)
if err != nil {
return none
}

for _, obj := range objs {
curMigration := obj.(*k6tv1.VirtualMachineInstanceMigration)
if curMigration.UID != migrationUID {
continue
}

return curMigration.Name
}

return none
}
84 changes: 84 additions & 0 deletions pkg/monitoring/metrics/virt-controller/vmistats_collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,81 @@ var _ = Describe("VMI Stats Collector", func() {
Expect(metrics[1].Labels).To(Equal([]string{"testNode", "test-ns", "testvmi", "networkA", "", "InternalIP"}))
})
})

Context("VMI migration start and end time metrics", func() {
now := metav1.Unix(1000, 0)
nowFloatValue := float64(now.Unix())

Describe("kubevirt_vmi_migration_start_time and kubevirt_vmi_migration_end_time metrics", func() {
It("should not create migration metrics for a VMI with no migration state", func() {
vmi := &k6tv1.VirtualMachineInstance{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test-ns",
Name: "testvmi",
},
Status: k6tv1.VirtualMachineInstanceStatus{
NodeName: "testNode",
},
}

metrics := collectVMIMigrationTime(vmi)
Expect(metrics).To(BeEmpty())
})

It("should create kubevirt_vmi_migration_start_time metric for a migration in progress", func() {
vmi := &k6tv1.VirtualMachineInstance{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test-ns",
Name: "testvmi",
},
Status: k6tv1.VirtualMachineInstanceStatus{
NodeName: "testNode",
MigrationState: &k6tv1.VirtualMachineInstanceMigrationState{
MigrationUID: "test-migration-uid",
StartTimestamp: &now,
},
},
}

metrics := collectVMIMigrationTime(vmi)
Expect(metrics).To(HaveLen(1))

Expect(metrics[0].Metric.GetOpts().Name).To(ContainSubstring("kubevirt_vmi_migration_start_time"))
Expect(metrics[0].Value).To(BeEquivalentTo(nowFloatValue))
Expect(metrics[0].Labels).To(Equal([]string{"testNode", "test-ns", "testvmi", "test-migration"}))
})

It("should create kubevirt_vmi_migration_end_time metric for a completedmigration", func() {
vmi := &k6tv1.VirtualMachineInstance{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test-ns",
Name: "testvmi",
},
Status: k6tv1.VirtualMachineInstanceStatus{
NodeName: "testNode",
MigrationState: &k6tv1.VirtualMachineInstanceMigrationState{
MigrationUID: "test-migration-uid",
StartTimestamp: &now,
EndTimestamp: &now,
Completed: true,
Failed: false,
},
},
}

metrics := collectVMIMigrationTime(vmi)
Expect(metrics).To(HaveLen(2))

Expect(metrics[0].Metric.GetOpts().Name).To(ContainSubstring("kubevirt_vmi_migration_start_time"))
Expect(metrics[0].Value).To(BeEquivalentTo(nowFloatValue))
Expect(metrics[0].Labels).To(Equal([]string{"testNode", "test-ns", "testvmi", "test-migration"}))

Expect(metrics[1].Metric.GetOpts().Name).To(ContainSubstring("kubevirt_vmi_migration_end_time"))
Expect(metrics[1].Value).To(BeEquivalentTo(nowFloatValue))
Expect(metrics[1].Labels).To(Equal([]string{"testNode", "test-ns", "testvmi", "test-migration", "succeeded"}))
})
})
})
})

func interfacesFor(values [][]string) []k6tv1.VirtualMachineInstanceNetworkInterface {
Expand Down Expand Up @@ -447,6 +522,7 @@ func setupTestCollector() {
preferenceInformer, _ = testutils.NewFakeInformerFor(&instancetypev1beta1.VirtualMachinePreference{})
clusterPreferenceInformer, _ = testutils.NewFakeInformerFor(&instancetypev1beta1.VirtualMachineClusterPreference{})
kvPodInformer, _ = testutils.NewFakeInformerFor(&k8sv1.Pod{})
vmiMigrationInformer, _ = testutils.NewFakeInformerFor(&k6tv1.VirtualMachineInstanceMigration{})

_ = instanceTypeInformer.GetStore().Add(&instancetypev1beta1.VirtualMachineInstancetype{
ObjectMeta: newObjectMetaForInstancetypes("i-managed", "kubevirt.io"),
Expand Down Expand Up @@ -476,6 +552,14 @@ func setupTestCollector() {
_ = kvPodInformer.GetStore().Add(&k8sv1.Pod{
ObjectMeta: newPodMetaForInformer("virt-launcher-testpod", "test-ns", "test-vmi-uid"),
})

_ = vmiMigrationInformer.GetStore().Add(&k6tv1.VirtualMachineInstanceMigration{
ObjectMeta: metav1.ObjectMeta{
Name: "test-migration",
Namespace: "test-ns",
UID: "test-migration-uid",
},
})
}

func newObjectMetaForInstancetypes(name, vendor string) metav1.ObjectMeta {
Expand Down
2 changes: 2 additions & 0 deletions tests/monitoring/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ var _ = Describe("[sig-monitoring]Metrics", decorators.SigMonitoring, func() {
"kubevirt_vmi_migration_dirty_memory_rate_bytes": true,
"kubevirt_vmi_migration_disk_transfer_rate_bytes": true,
"kubevirt_vmi_migration_data_total_bytes": true,
"kubevirt_vmi_migration_start_time_seconds": true,
"kubevirt_vmi_migration_end_time_seconds": true,
}

It("should contain virt components metrics", func() {
Expand Down

0 comments on commit 7b98f29

Please sign in to comment.