Skip to content

Commit

Permalink
Collect VMI OS info from the Guest agent
Browse files Browse the repository at this point in the history
Signed-off-by: assafad <aadmi@redhat.com>
  • Loading branch information
assafad committed Apr 17, 2024
1 parent 0e6776f commit 1b32659
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 35 deletions.
63 changes: 48 additions & 15 deletions pkg/monitoring/vmistats/collector.go
Expand Up @@ -52,7 +52,8 @@ var (
"kubevirt_vmi_phase_count",
"VMI phase.",
[]string{
"node", "phase", "os", "workload", "flavor", "instance_type", "preference",
"node", "phase", "os", "workload", "flavor", "instance_type", "preference", "guest_os_kernel_release",
"guest_os_machine", "guest_os_name", "guest_os_version_id",
},
nil,
)
Expand All @@ -76,13 +77,17 @@ var (
)

type vmiCountMetric struct {
Phase string
OS string
Workload string
Flavor string
InstanceType string
Preference string
NodeName string
Phase string
OS string
Workload string
Flavor string
InstanceType string
Preference string
NodeName string
GuestOSKernelRelease string
GuestOSMachine string
GuestOSName string
GuestOSVersionID string
}

type VMICollector struct {
Expand Down Expand Up @@ -215,18 +220,45 @@ func (co *VMICollector) setPreferenceFromAnnotations(vmc *vmiCountMetric, annota
}
}

func updateFromGuestOSInfo(vmc *vmiCountMetric, guestOSInfo k6tv1.VirtualMachineInstanceGuestOSInfo) {
if guestOSInfo == (k6tv1.VirtualMachineInstanceGuestOSInfo{}) {
return
}

if guestOSInfo.KernelRelease != "" {
vmc.GuestOSKernelRelease = guestOSInfo.KernelRelease
}

if guestOSInfo.Machine != "" {
vmc.GuestOSMachine = guestOSInfo.Machine
}

if guestOSInfo.Name != "" {
vmc.GuestOSName = guestOSInfo.Name
}

if guestOSInfo.VersionID != "" {
vmc.GuestOSVersionID = guestOSInfo.VersionID
}
}

func (co *VMICollector) newVMICountMetric(vmi *k6tv1.VirtualMachineInstance) vmiCountMetric {
vmc := vmiCountMetric{
Phase: strings.ToLower(string(vmi.Status.Phase)),
OS: none,
Workload: none,
Flavor: none,
InstanceType: none,
Preference: none,
NodeName: vmi.Status.NodeName,
Phase: strings.ToLower(string(vmi.Status.Phase)),
OS: none,
Workload: none,
Flavor: none,
InstanceType: none,
Preference: none,
GuestOSKernelRelease: none,
GuestOSMachine: none,
GuestOSName: none,
GuestOSVersionID: none,
NodeName: vmi.Status.NodeName,
}

co.UpdateFromAnnotations(&vmc, vmi.Annotations)
updateFromGuestOSInfo(&vmc, vmi.Status.GuestOSInfo)

return vmc
}
Expand All @@ -251,6 +283,7 @@ func (co *VMICollector) updateVMIsPhase(vmis []*k6tv1.VirtualMachineInstance, ch
vmiCountDesc, prometheus.GaugeValue,
float64(count),
vmc.NodeName, vmc.Phase, vmc.OS, vmc.Workload, vmc.Flavor, vmc.InstanceType, vmc.Preference,
vmc.GuestOSKernelRelease, vmc.GuestOSMachine, vmc.GuestOSName, vmc.GuestOSVersionID,
)
if err != nil {
log.Log.Reason(err).Errorf("Failed to create metric for VMIs phase")
Expand Down
58 changes: 38 additions & 20 deletions pkg/monitoring/vmistats/collector_test.go
Expand Up @@ -158,6 +158,12 @@ var _ = Describe("Utility functions", func() {
},
Status: k6tv1.VirtualMachineInstanceStatus{
Phase: "Pending",
GuestOSInfo: k6tv1.VirtualMachineInstanceGuestOSInfo{
KernelRelease: "6.5.6-300.fc39.x86_64",
Machine: "x86_64",
Name: "Fedora Linux",
VersionID: "39",
},
},
},
{
Expand Down Expand Up @@ -195,28 +201,40 @@ var _ = Describe("Utility functions", func() {
Expect(countMap).To(HaveLen(3))

running := vmiCountMetric{
Phase: "running",
OS: "centos8",
Workload: "server",
Flavor: "tiny",
InstanceType: "<none>",
Preference: "<none>",
Phase: "running",
OS: "centos8",
Workload: "server",
Flavor: "tiny",
GuestOSKernelRelease: "<none>",
GuestOSMachine: "<none>",
GuestOSName: "<none>",
GuestOSVersionID: "<none>",
InstanceType: "<none>",
Preference: "<none>",
}
pending := vmiCountMetric{
Phase: "pending",
OS: "fedora33",
Workload: "workstation",
Flavor: "large",
InstanceType: "<none>",
Preference: "<none>",
Phase: "pending",
OS: "fedora33",
Workload: "workstation",
Flavor: "large",
InstanceType: "<none>",
Preference: "<none>",
GuestOSKernelRelease: "6.5.6-300.fc39.x86_64",
GuestOSMachine: "x86_64",
GuestOSName: "Fedora Linux",
GuestOSVersionID: "39",
}
scheduling := vmiCountMetric{
Phase: "scheduling",
OS: "centos7",
Workload: "server",
Flavor: "medium",
InstanceType: "<none>",
Preference: "<none>",
Phase: "scheduling",
OS: "centos7",
Workload: "server",
Flavor: "medium",
GuestOSKernelRelease: "<none>",
GuestOSMachine: "<none>",
GuestOSName: "<none>",
GuestOSVersionID: "<none>",
InstanceType: "<none>",
Preference: "<none>",
}
bogus := vmiCountMetric{
Phase: "bogus",
Expand Down Expand Up @@ -254,7 +272,7 @@ var _ = Describe("Utility functions", func() {
Expect(result).ToNot(BeNil())
Expect(result.Desc().String()).To(ContainSubstring("kubevirt_vmi_phase_count"))
Expect(dto.Gauge.GetValue()).To(BeEquivalentTo(1))
Expect(dto.Label).To(HaveLen(7))
Expect(dto.Label).To(HaveLen(11))
for _, pair := range dto.Label {
if pair.GetName() == "instance_type" {
Expect(pair.GetValue()).To(Equal(expected))
Expand Down Expand Up @@ -297,7 +315,7 @@ var _ = Describe("Utility functions", func() {
Expect(result).ToNot(BeNil())
Expect(result.Desc().String()).To(ContainSubstring("kubevirt_vmi_phase_count"))
Expect(dto.Gauge.GetValue()).To(BeEquivalentTo(1))
Expect(dto.Label).To(HaveLen(7))
Expect(dto.Label).To(HaveLen(11))
for _, pair := range dto.Label {
if pair.GetName() == "preference" {
Expect(pair.GetValue()).To(Equal(expected))
Expand Down
2 changes: 2 additions & 0 deletions tests/monitoring/BUILD.bazel
Expand Up @@ -21,10 +21,12 @@ go_library(
"//tests/libnode:go_default_library",
"//tests/libvmi:go_default_library",
"//tests/libwait:go_default_library",
"//tests/testsuite:go_default_library",
"//tests/util:go_default_library",
"//vendor/github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1:go_default_library",
"//vendor/github.com/onsi/ginkgo/v2:go_default_library",
"//vendor/github.com/onsi/gomega:go_default_library",
"//vendor/github.com/onsi/gomega/gstruct:go_default_library",
"//vendor/github.com/prometheus/client_golang/api/prometheus/v1:go_default_library",
"//vendor/k8s.io/api/autoscaling/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
Expand Down
39 changes: 39 additions & 0 deletions tests/monitoring/monitoring.go
Expand Up @@ -46,6 +46,7 @@ import (
"kubevirt.io/kubevirt/tests/framework/checks"
"kubevirt.io/kubevirt/tests/libvmi"
"kubevirt.io/kubevirt/tests/libwait"
"kubevirt.io/kubevirt/tests/testsuite"
"kubevirt.io/kubevirt/tests/util"

autoscalingv1 "k8s.io/api/autoscaling/v1"
Expand All @@ -56,6 +57,7 @@ import (

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"

v1 "kubevirt.io/api/core/v1"
"kubevirt.io/client-go/kubecli"
Expand Down Expand Up @@ -637,6 +639,25 @@ var _ = Describe("[Serial][sig-monitoring]Prometheus Alerts", Serial, decorators
})
})

Context("VM metrics that are based on the guest agent", func() {
It("should have kubevirt_vmi_phase_count correctly configured with guest OS labels", func() {
agentVMI := createAgentVMI()
Expect(agentVMI.Status.GuestOSInfo.KernelRelease).ToNot(BeEmpty())
Expect(agentVMI.Status.GuestOSInfo.Machine).ToNot(BeEmpty())
Expect(agentVMI.Status.GuestOSInfo.Name).ToNot(BeEmpty())
Expect(agentVMI.Status.GuestOSInfo.VersionID).ToNot(BeEmpty())

labels := map[string]string{
"guest_os_kernel_release": agentVMI.Status.GuestOSInfo.KernelRelease,
"guest_os_machine": agentVMI.Status.GuestOSInfo.Machine,
"guest_os_name": agentVMI.Status.GuestOSInfo.Name,
"guest_os_version_id": agentVMI.Status.GuestOSInfo.VersionID,
}

waitForMetricValueWithLabels(virtClient, "kubevirt_vmi_phase_count", 1, labels)
})
})

Context("Migration Alerts", decorators.SigComputeMigrations, func() {
It("KubeVirtVMIExcessiveMigrations should be triggered when a VMI has been migrated more than 12 times during the last 24 hours", func() {
By("Starting the VirtualMachineInstance")
Expand Down Expand Up @@ -681,3 +702,21 @@ func checkRequiredLabels(rule promv1.Rule) {
ExpectWithOffset(1, rule.Labels).To(HaveKeyWithValue("kubernetes_operator_component", "kubevirt"),
fmt.Sprintf("%s kubernetes_operator_component label is missing or not valid", rule.Alert))
}

func createAgentVMI() *v1.VirtualMachineInstance {
virtClient := kubevirt.Client()
vmiAgentConnectedConditionMatcher := MatchFields(IgnoreExtras, Fields{"Type": Equal(v1.VirtualMachineInstanceAgentConnected)})
vmi := tests.RunVMIAndExpectLaunch(libvmi.NewFedora(libvmi.WithMasqueradeNetworking()...), 180)

var err error
var agentVMI *v1.VirtualMachineInstance

By("VMI has the guest agent connected condition")
Eventually(func() []v1.VirtualMachineInstanceCondition {
agentVMI, err = virtClient.VirtualMachineInstance(testsuite.GetTestNamespace(vmi)).Get(context.Background(), vmi.Name, &metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
return agentVMI.Status.Conditions
}, 240*time.Second, 1*time.Second).Should(ContainElement(vmiAgentConnectedConditionMatcher), "Should have agent connected condition")

return agentVMI
}

0 comments on commit 1b32659

Please sign in to comment.