Repository: RedHatQE/openshift-virtualization-tests
Pull request #437: Test for vnic_info metrics
Author: login: OhadRevah
Labels: name: verified
name: size/M
name: can-be-merged
name: branch-main
name: tox:verify-tc-requirement-polarion:passed
description: D/S Testing
name: lgtm-polarion-jenkins
name: tox:verify-bugs-are-open:passed
description: D/S Testing
name: approved-vsibirsk
Created: 2025-03-06T16:25:22Z | Merged: 2025-03-17T10:52:32Z

##### Short description:
Test for kubevirt_vm_vnic_info
and kubevirt_vm_vnic_info
##### More details:

##### What this PR does / why we need it:

##### Which issue(s) this PR fixes:

##### Special notes for reviewer:

##### jira-ticket:
<!--  full-ticket-url needs to be provided. This would add a link to the pull request to the jira and close it when the pull request is merged
If the task is not tracked by a Jira ticket, just write "NONE".
-->



Diff:
diff --git a/tests/observability/metrics/conftest.py b/tests/observability/metrics/conftest.py
index 471c75b0b3..7b046acd9e 100644
--- a/tests/observability/metrics/conftest.py
+++ b/tests/observability/metrics/conftest.py
@@ -20,6 +20,8 @@
 from timeout_sampler import TimeoutExpiredError, TimeoutSampler
 
 from tests.observability.metrics.constants import (
+    BINDING_NAME,
+    BINDING_TYPE,
     CNV_VMI_STATUS_RUNNING_COUNT,
     KUBEVIRT_API_REQUEST_DEPRECATED_TOTAL_WITH_VERSION_VERB_AND_RESOURCE,
     KUBEVIRT_CONSOLE_ACTIVE_CONNECTIONS_BY_VMI,
@@ -33,6 +35,7 @@
 from tests.observability.metrics.utils import (
     SINGLE_VM,
     ZERO_CPU_CORES,
+    binding_name_and_type_from_vm_or_vmi,
     disk_file_system_info,
     enable_swap_fedora_vm,
     fail_if_not_zero_restartcount,
@@ -1049,6 +1052,19 @@ def initiate_metric_value(request, prometheus):
     return get_metrics_value(prometheus=prometheus, metrics_name=request.param)
 
 
+@pytest.fixture()
+def vnic_info_from_vm_or_vmi(request, running_metric_vm):
+    vm_instance = (
+        running_metric_vm.vmi.instance.spec if request.param == "vmi" else running_metric_vm.instance.spec.template.spec
+    )
+    binding_name_and_type = binding_name_and_type_from_vm_or_vmi(vm_interface=vm_instance.domain.devices.interfaces[0])
+    return {
+        "vnic_name": vm_instance.networks[0].name,
+        BINDING_NAME: binding_name_and_type[BINDING_NAME],
+        BINDING_TYPE: binding_name_and_type[BINDING_TYPE],
+    }
+
+
 @pytest.fixture()
 def allocatable_nodes(nodes):
     return [node for node in nodes if node.instance.status.allocatable.memory != "0"]
diff --git a/tests/observability/metrics/constants.py b/tests/observability/metrics/constants.py
index ffa0a816cb..e8d3f77368 100644
--- a/tests/observability/metrics/constants.py
+++ b/tests/observability/metrics/constants.py
@@ -80,3 +80,5 @@
 KUBEVIRT_VMSNAPSHOT_PERSISTENTVOLUMECLAIM_LABELS = (
     "kubevirt_vmsnapshot_persistentvolumeclaim_labels{{vm_name='{vm_name}'}}"
 )
+BINDING_NAME = "binding_name"
+BINDING_TYPE = "binding_type"
diff --git a/tests/observability/metrics/test_vms_metrics.py b/tests/observability/metrics/test_vms_metrics.py
index 13868c0138..c971d9e28d 100644
--- a/tests/observability/metrics/test_vms_metrics.py
+++ b/tests/observability/metrics/test_vms_metrics.py
@@ -24,6 +24,7 @@
     expected_metric_labels_and_values,
     timestamp_to_seconds,
     validate_metric_value_within_range,
+    validate_vnic_info,
 )
 from tests.observability.utils import validate_metrics_value
 from tests.os_params import FEDORA_LATEST_LABELS, RHEL_LATEST
@@ -492,3 +493,28 @@ def test_metric_kubevirt_vmsnapshot_persistentvolumeclaim_labels(
             ),
             expected_labels_and_values=snapshot_labels_for_testing,
         )
+
+
+class TestVmVnicInfo:
+    @pytest.mark.parametrize(
+        "vnic_info_from_vm_or_vmi, query",
+        [
+            pytest.param(
+                "vm",
+                "kubevirt_vm_vnic_info{{name='{vm_name}'}}",
+                marks=pytest.mark.polarion("CNV-11812"),
+            ),
+            pytest.param(
+                "vmi",
+                "kubevirt_vmi_vnic_info{{name='{vm_name}'}}",
+                marks=pytest.mark.polarion("CNV-11811"),
+            ),
+        ],
+        indirect=["vnic_info_from_vm_or_vmi"],
+    )
+    def test_metric_kubevirt_vm_vnic_info(self, prometheus, running_metric_vm, vnic_info_from_vm_or_vmi, query):
+        validate_vnic_info(
+            prometheus=prometheus,
+            vnic_info_to_compare=vnic_info_from_vm_or_vmi,
+            metric_name=query.format(vm_name=running_metric_vm.name),
+        )
diff --git a/tests/observability/metrics/utils.py b/tests/observability/metrics/utils.py
index 7b0247449b..c96e4faa45 100644
--- a/tests/observability/metrics/utils.py
+++ b/tests/observability/metrics/utils.py
@@ -20,6 +20,8 @@
 
 from tests.observability.constants import KUBEVIRT_VIRT_OPERATOR_READY
 from tests.observability.metrics.constants import (
+    BINDING_NAME,
+    BINDING_TYPE,
     GO_VERSION_STR,
     INSTANCE_TYPE_LABELS,
     KUBE_VERSION_STR,
@@ -1230,3 +1232,22 @@ def validate_metric_value_with_round_down(
     except TimeoutExpiredError:
         LOGGER.info(f"Metric int value of: {metric_name} is: {sample}, expected value:{expected_value}")
         raise
+
+
+def binding_name_and_type_from_vm_or_vmi(vm_interface: dict[str, str]) -> dict[str, str]:
+    binding_name_and_type = None
+    for binding_name in ["masquerade", "bridge", "sriov"]:
+        if vm_interface.get(binding_name):
+            binding_name_and_type = {BINDING_NAME: binding_name, BINDING_TYPE: "core"}
+    assert binding_name_and_type, f"vm interface {vm_interface} has not valid binding name."
+    return binding_name_and_type
+
+
+def validate_vnic_info(prometheus: Prometheus, vnic_info_to_compare: dict[str, str], metric_name: str) -> None:
+    vnic_info_metric_result = prometheus.query_sampler(query=metric_name)[0].get("metric")
+    mismatch_vnic_info = {}
+    for info, expected_value in vnic_info_to_compare.items():
+        actual_value = vnic_info_metric_result.get(info)
+        if actual_value != expected_value:
+            mismatch_vnic_info[info] = {f"Expected: {expected_value}", f"Actual: {actual_value}"}
+    assert not mismatch_vnic_info, f"There is a mismatch between expected and actual results:\n {mismatch_vnic_info}"


Reviews:
user: login: polarion-jenkins
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-06T16:28:20Z
user: login: polarion-jenkins
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-03-09T09:35:25Z
user: login: hmeir
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-09T14:04:57Z
user: login: vsibirsk
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-09T15:56:46Z
user: login: polarion-jenkins
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-03-09T17:51:34Z
user: login: hmeir
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T11:36:51Z
user: login: OhadRevah
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T12:02:58Z
user: login: OhadRevah
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T12:13:17Z
user: login: polarion-jenkins
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-03-10T12:19:33Z
user: login: vsibirsk
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-10T17:16:54Z
user: login: hmeir
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T08:54:28Z
user: login: polarion-jenkins
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-11T11:04:35Z
user: login: polarion-jenkins
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-03-11T11:11:19Z
user: login: hmeir
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T13:35:01Z
user: login: polarion-jenkins
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-11T15:25:39Z
user: login: vsibirsk
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-12T09:29:52Z
user: login: hmeir
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T10:21:26Z
user: login: polarion-jenkins
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-03-12T10:53:40Z
user: login: polarion-jenkins
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-03-12T11:01:05Z
user: login: polarion-jenkins
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-12T11:05:59Z
user: login: polarion-jenkins
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-12T13:25:28Z
user: login: Ahmad-Hafe
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T18:19:46Z
user: login: vsibirsk
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-13T08:09:17Z
user: login: polarion-jenkins
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-03-13T09:10:44Z
user: login: polarion-jenkins
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-13T09:40:57Z
user: login: polarion-jenkins
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-03-16T14:44:36Z
user: login: vsibirsk
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-17T10:46:32Z
user: login: polarion-jenkins
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-17T10:46:41Z
user: login: vsibirsk
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-17T10:49:19Z
user: login: polarion-jenkins
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-17T10:51:42Z

Review comments:
diff_hunk: @@ -1046,3 +1047,23 @@ def deleted_ssp_operator_pod(admin_client, hco_namespace):
 @pytest.fixture(scope="class")
 def initiate_metric_value(request, prometheus):
     return get_metrics_value(prometheus=prometheus, metrics_name=request.param)
+
+
+@pytest.fixture(scope="class")
+def vnic_info_from_vm_and_vmi(vm_for_test):
+    vmi_instance = vm_for_test.vmi.instance
+    vm_instance = vm_for_test.instance
+    vm_instance_binding_name_and_type = binding_name_and_type_from_vm_or_vmi(vm=vm_instance)
+    vmi_instance_binding_name_and_type = binding_name_and_type_from_vm_or_vmi(vm=vmi_instance)
+    return {
+        "vm": {
+            "vnic_name": vm_instance.spec.template.spec.networks[0].name,
+            "binding_name": vm_instance_binding_name_and_type["binding_name"],
+            "binding_type": vm_instance_binding_name_and_type["binding_type"],
+        },
+        "vmi": {
+            "vnic_name": vmi_instance.spec.networks[0].name,
+            "binding_name": vmi_instance_binding_name_and_type["binding_name"],
+            "binding_type": vmi_instance_binding_name_and_type["binding_type"],
+        },
+    }
path: tests/observability/metrics/conftest.py
user: login: hmeir
body: A lot of duplication here(strings, perfoming almost the same for both vm/vmi)
Please refactor(maybe a function?)

created_at: 2025-03-09T14:01:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -492,3 +493,32 @@ def test_metric_kubevirt_vmsnapshot_persistentvolumeclaim_labels(
             ),
             expected_labels_and_values=snapshot_labels_for_testing,
         )
+
+
+class TestVmVnicInfo:
+    @pytest.mark.parametrize(
+        "vm_for_test, query, vm_or_vmi",
+        [
+            pytest.param(
+                "vnic-testing-vm",
+                "kubevirt_vm_vnic_info{{name='{vm_name}'}}",
+                "vm",
+                marks=pytest.mark.polarion("CNV-11812"),
+            ),
+            pytest.param(
+                "vnic-testing-vm",
+                "kubevirt_vmi_vnic_info{{name='{vm_name}'}}",
+                "vmi",
+                marks=pytest.mark.polarion("CNV-11811"),
+            ),
+        ],
+        indirect=["vm_for_test"],
+    )
+    def test_metric_kubevirt_vm_vnic_info(self, prometheus, vm_for_test, vnic_info_from_vm_and_vmi, query, vm_or_vmi):
+        validate_vnic_info(
+            prometheus=prometheus,
+            vnic_info_to_compare=vnic_info_from_vm_and_vmi["vm"]
+            if vm_or_vmi == "vm"
+            else vnic_info_from_vm_and_vmi["vmi"],
path: tests/observability/metrics/test_vms_metrics.py
user: login: hmeir
body: kinda redudant since the param "vm_or_vmi" is passing the same strings
created_at: 2025-03-09T14:02:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -492,3 +493,32 @@ def test_metric_kubevirt_vmsnapshot_persistentvolumeclaim_labels(
             ),
             expected_labels_and_values=snapshot_labels_for_testing,
         )
+
+
+class TestVmVnicInfo:
+    @pytest.mark.parametrize(
+        "vm_for_test, query, vm_or_vmi",
+        [
+            pytest.param(
+                "vnic-testing-vm",
+                "kubevirt_vm_vnic_info{{name='{vm_name}'}}",
+                "vm",
+                marks=pytest.mark.polarion("CNV-11812"),
+            ),
+            pytest.param(
+                "vnic-testing-vm",
path: tests/observability/metrics/test_vms_metrics.py
user: login: hmeir
body: should the vm's have the same name?
created_at: 2025-03-09T14:03:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1230,3 +1230,32 @@ def validate_metric_value_with_round_down(
     except TimeoutExpiredError:
         LOGGER.info(f"Metric int value of: {metric_name} is: {sample}, expected value:{expected_value}")
         raise
+
+
+def binding_name_and_type_from_vm_or_vmi(vm: ResourceInstance) -> dict[str, str]:
path: tests/observability/metrics/utils.py
user: login: hmeir
body: if its a resource instance - should we call the arg "vm"?
created_at: 2025-03-09T14:04:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -492,3 +493,32 @@ def test_metric_kubevirt_vmsnapshot_persistentvolumeclaim_labels(
             ),
             expected_labels_and_values=snapshot_labels_for_testing,
         )
+
+
+class TestVmVnicInfo:
+    @pytest.mark.parametrize(
+        "vm_for_test, query, vm_or_vmi",
+        [
+            pytest.param(
+                "vnic-testing-vm",
+                "kubevirt_vm_vnic_info{{name='{vm_name}'}}",
+                "vm",
+                marks=pytest.mark.polarion("CNV-11812"),
+            ),
+            pytest.param(
+                "vnic-testing-vm",
path: tests/observability/metrics/test_vms_metrics.py
user: login: vsibirsk
body: you don't need a new vm here. same VM can be reused for both tests
created_at: 2025-03-09T15:54:39Z
author_association: COLLABORATOR
diff_hunk: @@ -1046,3 +1047,23 @@ def deleted_ssp_operator_pod(admin_client, hco_namespace):
 @pytest.fixture(scope="class")
 def initiate_metric_value(request, prometheus):
     return get_metrics_value(prometheus=prometheus, metrics_name=request.param)
+
+
+@pytest.fixture(scope="class")
+def vnic_info_from_vm_and_vmi(vm_for_test):
+    vmi_instance = vm_for_test.vmi.instance
+    vm_instance = vm_for_test.instance
+    vm_instance_binding_name_and_type = binding_name_and_type_from_vm_or_vmi(vm=vm_instance)
+    vmi_instance_binding_name_and_type = binding_name_and_type_from_vm_or_vmi(vm=vmi_instance)
+    return {
+        "vm": {
+            "vnic_name": vm_instance.spec.template.spec.networks[0].name,
+            "binding_name": vm_instance_binding_name_and_type["binding_name"],
+            "binding_type": vm_instance_binding_name_and_type["binding_type"],
+        },
+        "vmi": {
+            "vnic_name": vmi_instance.spec.networks[0].name,
+            "binding_name": vmi_instance_binding_name_and_type["binding_name"],
+            "binding_type": vmi_instance_binding_name_and_type["binding_type"],
+        },
+    }
path: tests/observability/metrics/conftest.py
user: login: vsibirsk
body: send param via `requset` can do the trick here i believe
created_at: 2025-03-09T15:56:41Z
author_association: COLLABORATOR
diff_hunk: @@ -492,3 +493,37 @@ def test_metric_kubevirt_vmsnapshot_persistentvolumeclaim_labels(
             ),
             expected_labels_and_values=snapshot_labels_for_testing,
         )
+
+
+@pytest.mark.parametrize(
+    "vm_for_test",
+    [
+        pytest.param(
+            "vnic-testing-vm",
+        ),
+    ],
+    indirect=True,
+)
+@pytest.mark.usefixtures("vm_for_test")
path: tests/observability/metrics/test_vms_metrics.py
user: login: hmeir
body: is required?
created_at: 2025-03-10T11:26:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -492,3 +493,37 @@ def test_metric_kubevirt_vmsnapshot_persistentvolumeclaim_labels(
             ),
             expected_labels_and_values=snapshot_labels_for_testing,
         )
+
+
+@pytest.mark.parametrize(
+    "vm_for_test",
+    [
+        pytest.param(
+            "vnic-testing-vm",
+        ),
+    ],
+    indirect=True,
+)
+@pytest.mark.usefixtures("vm_for_test")
+class TestVmVnicInfo:
+    @pytest.mark.parametrize(
+        "query, vm_or_vmi",
+        [
+            pytest.param(
+                "kubevirt_vm_vnic_info{{name='{vm_name}'}}",
+                "vm",
+                marks=pytest.mark.polarion("CNV-11812"),
+            ),
+            pytest.param(
+                "kubevirt_vmi_vnic_info{{name='{vm_name}'}}",
+                "vmi",
+                marks=pytest.mark.polarion("CNV-11811"),
+            ),
+        ],
+    )
+    def test_metric_kubevirt_vm_vnic_info(self, prometheus, vm_for_test, vnic_info_from_vm_and_vmi, query, vm_or_vmi):
+        validate_vnic_info(
+            prometheus=prometheus,
+            vnic_info_to_compare=vnic_info_from_vm_and_vmi["vm" if vm_or_vmi == "vm" else "vmi"],
path: tests/observability/metrics/test_vms_metrics.py
user: login: hmeir
body: ```suggestion
            vnic_info_to_compare=vnic_info_from_vm_and_vmi[vm_or_vmi],
```
created_at: 2025-03-10T11:26:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1230,3 +1230,32 @@ def validate_metric_value_with_round_down(
     except TimeoutExpiredError:
         LOGGER.info(f"Metric int value of: {metric_name} is: {sample}, expected value:{expected_value}")
         raise
+
+
+def binding_name_and_type_from_vm_or_vmi(vm: ResourceInstance) -> dict[str, str]:
path: tests/observability/metrics/utils.py
user: login: hmeir
body: not addressed
created_at: 2025-03-10T11:28:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1230,3 +1230,32 @@ def validate_metric_value_with_round_down(
     except TimeoutExpiredError:
         LOGGER.info(f"Metric int value of: {metric_name} is: {sample}, expected value:{expected_value}")
         raise
+
+
+def binding_name_and_type_from_vm_or_vmi(vm: ResourceInstance) -> dict[str, str]:
+    binding_names_and_types = {
+        "masquerade": "core",
+        "bridge": "core",
+        "sriov": "core",
+    }
+    vm_interface = None
+    if vm.kind == "VirtualMachineInstance":
+        vm_interface = vm.spec.domain.devices.interfaces[0]
+    elif vm.kind == "VirtualMachine":
+        vm_interface = vm.spec.template.spec.domain.devices.interfaces[0]
+    if vm_interface:
+        for binding_name in binding_names_and_types.keys():
+            if vm_interface.get(binding_name):
+                return {"binding_name": binding_name, "binding_type": binding_names_and_types[binding_name]}
path: tests/observability/metrics/utils.py
user: login: hmeir
body: if the value for all keys is fixed("core"),  maybe use a list for the keys instead of dict?
created_at: 2025-03-10T11:31:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -492,3 +493,37 @@ def test_metric_kubevirt_vmsnapshot_persistentvolumeclaim_labels(
             ),
             expected_labels_and_values=snapshot_labels_for_testing,
         )
+
+
+@pytest.mark.parametrize(
+    "vm_for_test",
+    [
+        pytest.param(
+            "vnic-testing-vm",
+        ),
+    ],
+    indirect=True,
path: tests/observability/metrics/test_vms_metrics.py
user: login: hmeir
body: Why paramatrize twice?
created_at: 2025-03-10T11:34:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1230,3 +1230,32 @@ def validate_metric_value_with_round_down(
     except TimeoutExpiredError:
         LOGGER.info(f"Metric int value of: {metric_name} is: {sample}, expected value:{expected_value}")
         raise
+
+
+def binding_name_and_type_from_vm_or_vmi(vm: ResourceInstance) -> dict[str, str]:
+    binding_names_and_types = {
+        "masquerade": "core",
+        "bridge": "core",
+        "sriov": "core",
+    }
+    vm_interface = None
+    if vm.kind == "VirtualMachineInstance":
+        vm_interface = vm.spec.domain.devices.interfaces[0]
+    elif vm.kind == "VirtualMachine":
+        vm_interface = vm.spec.template.spec.domain.devices.interfaces[0]
+    if vm_interface:
+        for binding_name in binding_names_and_types.keys():
+            if vm_interface.get(binding_name):
+                return {"binding_name": binding_name, "binding_type": binding_names_and_types[binding_name]}
+    return {}
path: tests/observability/metrics/utils.py
user: login: hmeir
body: if no vm_interface, should we fail?
created_at: 2025-03-10T11:36:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -492,3 +493,37 @@ def test_metric_kubevirt_vmsnapshot_persistentvolumeclaim_labels(
             ),
             expected_labels_and_values=snapshot_labels_for_testing,
         )
+
+
+@pytest.mark.parametrize(
+    "vm_for_test",
+    [
+        pytest.param(
+            "vnic-testing-vm",
+        ),
+    ],
+    indirect=True,
+)
+@pytest.mark.usefixtures("vm_for_test")
path: tests/observability/metrics/test_vms_metrics.py
user: login: OhadRevah
body: Yes
created_at: 2025-03-10T12:02:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1230,3 +1230,32 @@ def validate_metric_value_with_round_down(
     except TimeoutExpiredError:
         LOGGER.info(f"Metric int value of: {metric_name} is: {sample}, expected value:{expected_value}")
         raise
+
+
+def binding_name_and_type_from_vm_or_vmi(vm: ResourceInstance) -> dict[str, str]:
+    binding_names_and_types = {
+        "masquerade": "core",
+        "bridge": "core",
+        "sriov": "core",
+    }
+    vm_interface = None
+    if vm.kind == "VirtualMachineInstance":
+        vm_interface = vm.spec.domain.devices.interfaces[0]
+    elif vm.kind == "VirtualMachine":
+        vm_interface = vm.spec.template.spec.domain.devices.interfaces[0]
+    if vm_interface:
+        for binding_name in binding_names_and_types.keys():
+            if vm_interface.get(binding_name):
+                return {"binding_name": binding_name, "binding_type": binding_names_and_types[binding_name]}
+    return {}
path: tests/observability/metrics/utils.py
user: login: OhadRevah
body: It will fail if it will not succeed to get the field with AtrributeError or index out of range.
created_at: 2025-03-10T12:13:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -492,3 +493,37 @@ def test_metric_kubevirt_vmsnapshot_persistentvolumeclaim_labels(
             ),
             expected_labels_and_values=snapshot_labels_for_testing,
         )
+
+
+@pytest.mark.parametrize(
+    "vm_for_test",
+    [
+        pytest.param(
+            "vnic-testing-vm",
+        ),
+    ],
+    indirect=True,
+)
+@pytest.mark.usefixtures("vm_for_test")
path: tests/observability/metrics/test_vms_metrics.py
user: login: vsibirsk
body: why? it's anyway triggered in [`def test_metric_kubevirt_vm_vnic_info`](https://github.com/RedHatQE/openshift-virtualization-tests/pull/437/files#diff-27fed814cb26a8e06b32f2af256386a74b95ee10ffaabbb38ce656354593a7c5R524)
created_at: 2025-03-10T17:02:50Z
author_association: COLLABORATOR
diff_hunk: @@ -1230,3 +1231,27 @@ def validate_metric_value_with_round_down(
     except TimeoutExpiredError:
         LOGGER.info(f"Metric int value of: {metric_name} is: {sample}, expected value:{expected_value}")
         raise
+
+
+def binding_name_and_type_from_vm_or_vmi(vm: VirtualMachineInstance | VirtualMachine) -> dict[str, str]:
+    vm_interface = None
+    if vm.kind == "VirtualMachineInstance":
+        vm_interface = vm.spec.domain.devices.interfaces[0]
+    elif vm.kind == "VirtualMachine":
+        vm_interface = vm.spec.template.spec.domain.devices.interfaces[0]
+    if vm_interface:
+        for binding_name in ["masquerade", "bridge", "sriov"]:
+            if vm_interface.get(binding_name):
+                return {"binding_name": binding_name, "binding_type": "core"}
+    return {}
path: tests/observability/metrics/utils.py
user: login: vsibirsk
body: the function should just take `vm_interface` param
created_at: 2025-03-10T17:11:10Z
author_association: COLLABORATOR
diff_hunk: @@ -1230,3 +1230,32 @@ def validate_metric_value_with_round_down(
     except TimeoutExpiredError:
         LOGGER.info(f"Metric int value of: {metric_name} is: {sample}, expected value:{expected_value}")
         raise
+
+
+def binding_name_and_type_from_vm_or_vmi(vm: ResourceInstance) -> dict[str, str]:
+    binding_names_and_types = {
+        "masquerade": "core",
+        "bridge": "core",
+        "sriov": "core",
+    }
+    vm_interface = None
+    if vm.kind == "VirtualMachineInstance":
+        vm_interface = vm.spec.domain.devices.interfaces[0]
+    elif vm.kind == "VirtualMachine":
+        vm_interface = vm.spec.template.spec.domain.devices.interfaces[0]
+    if vm_interface:
+        for binding_name in binding_names_and_types.keys():
+            if vm_interface.get(binding_name):
+                return {"binding_name": binding_name, "binding_type": binding_names_and_types[binding_name]}
+    return {}
path: tests/observability/metrics/utils.py
user: login: vsibirsk
body: it should be an assert with proper error msg
created_at: 2025-03-10T17:12:22Z
author_association: COLLABORATOR
diff_hunk: @@ -1046,3 +1050,23 @@ def deleted_ssp_operator_pod(admin_client, hco_namespace):
 @pytest.fixture(scope="class")
 def initiate_metric_value(request, prometheus):
     return get_metrics_value(prometheus=prometheus, metrics_name=request.param)
+
+
+@pytest.fixture(scope="class")
+def vnic_info_from_vm_and_vmi(vm_for_test):
+    vmi_instance = vm_for_test.vmi.instance
+    vm_instance = vm_for_test.instance
+    vm_instance_binding_name_and_type = binding_name_and_type_from_vm_or_vmi(vm=vm_instance)
+    vmi_instance_binding_name_and_type = binding_name_and_type_from_vm_or_vmi(vm=vmi_instance)
+    return {
+        "vm": {
+            VNIC_NAME: vm_instance.spec.template.spec.networks[0].name,
+            BINDING_NAME: vm_instance_binding_name_and_type[BINDING_NAME],
+            BINDING_TYPE: vm_instance_binding_name_and_type[BINDING_TYPE],
+        },
+        "vmi": {
+            VNIC_NAME: vmi_instance.spec.networks[0].name,
+            BINDING_NAME: vmi_instance_binding_name_and_type[BINDING_NAME],
+            BINDING_TYPE: vmi_instance_binding_name_and_type[BINDING_TYPE],
+        },
+    }
path: tests/observability/metrics/conftest.py
user: login: vsibirsk
body: this should be parametrized via request and based on that return either vm info or vmi info
created_at: 2025-03-10T17:16:26Z
author_association: COLLABORATOR
diff_hunk: @@ -1230,3 +1231,27 @@ def validate_metric_value_with_round_down(
     except TimeoutExpiredError:
         LOGGER.info(f"Metric int value of: {metric_name} is: {sample}, expected value:{expected_value}")
         raise
+
+
+def binding_name_and_type_from_vm_or_vmi(vm: VirtualMachineInstance | VirtualMachine) -> dict[str, str]:
path: tests/observability/metrics/utils.py
user: login: hmeir
body: The name "vm" is misleading - it can be VMI as well
should be something like "resource"
created_at: 2025-03-11T08:54:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1046,3 +1049,21 @@ def deleted_ssp_operator_pod(admin_client, hco_namespace):
 @pytest.fixture(scope="class")
 def initiate_metric_value(request, prometheus):
     return get_metrics_value(prometheus=prometheus, metrics_name=request.param)
+
+
+@pytest.fixture(scope="class")
+def vnic_info_from_vm_and_vmi(request, vm_for_test):
+    vm_or_vmi = request.param
+    vm_instance = vm_for_test.vmi.instance if vm_or_vmi == "vmi" else vm_for_test.instance
+    binding_name_and_type = binding_name_and_type_from_vm_or_vmi(
+        vm_interface=vm_instance.spec.domain.devices.interfaces[0]
+        if vm_or_vmi == "vmi"
+        else vm_instance.spec.template.spec.domain.devices.interfaces[0]
+    )
path: tests/observability/metrics/conftest.py
user: login: hmeir
body: The only difference between vm and vmi path to the interfaces, 
is that vm have `spec.template` prefix

How about saving this prefix instead of `vm_instance`, and then pass to the function
`(instance prefix).domain.devices.interfaces[0]`?
created_at: 2025-03-11T13:33:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1046,3 +1049,21 @@ def deleted_ssp_operator_pod(admin_client, hco_namespace):
 @pytest.fixture(scope="class")
 def initiate_metric_value(request, prometheus):
     return get_metrics_value(prometheus=prometheus, metrics_name=request.param)
+
+
+@pytest.fixture(scope="class")
+def vnic_info_from_vm_and_vmi(request, vm_for_test):
+    vm_or_vmi = request.param
+    vm_instance = vm_for_test.vmi.instance if vm_or_vmi == "vmi" else vm_for_test.instance
+    binding_name_and_type = binding_name_and_type_from_vm_or_vmi(
+        vm_interface=vm_instance.spec.domain.devices.interfaces[0]
+        if vm_or_vmi == "vmi"
+        else vm_instance.spec.template.spec.domain.devices.interfaces[0]
+    )
+    return {
+        "vnic_name": vm_instance.spec.template.spec.networks[0].name
+        if vm_or_vmi == "vm"
+        else vm_instance.spec.networks[0].name,
path: tests/observability/metrics/conftest.py
user: login: hmeir
body: ditto
created_at: 2025-03-11T13:33:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1230,3 +1230,32 @@ def validate_metric_value_with_round_down(
     except TimeoutExpiredError:
         LOGGER.info(f"Metric int value of: {metric_name} is: {sample}, expected value:{expected_value}")
         raise
+
+
+def binding_name_and_type_from_vm_or_vmi(vm: ResourceInstance) -> dict[str, str]:
+    binding_names_and_types = {
+        "masquerade": "core",
+        "bridge": "core",
+        "sriov": "core",
+    }
+    vm_interface = None
+    if vm.kind == "VirtualMachineInstance":
+        vm_interface = vm.spec.domain.devices.interfaces[0]
+    elif vm.kind == "VirtualMachine":
+        vm_interface = vm.spec.template.spec.domain.devices.interfaces[0]
+    if vm_interface:
+        for binding_name in binding_names_and_types.keys():
+            if vm_interface.get(binding_name):
+                return {"binding_name": binding_name, "binding_type": binding_names_and_types[binding_name]}
+    return {}
path: tests/observability/metrics/utils.py
user: login: vsibirsk
body: please don't resolve not addressed comments
created_at: 2025-03-12T09:09:35Z
author_association: COLLABORATOR
diff_hunk: @@ -1046,3 +1049,15 @@ def deleted_ssp_operator_pod(admin_client, hco_namespace):
 @pytest.fixture(scope="class")
 def initiate_metric_value(request, prometheus):
     return get_metrics_value(prometheus=prometheus, metrics_name=request.param)
+
+
+@pytest.fixture(scope="class")
+def vnic_info_from_vm_and_vmi(request, vm_for_test):
path: tests/observability/metrics/conftest.py
user: login: vsibirsk
body: ```suggestion
def vnic_info_from_vm_or_vmi(request, vm_for_test):
```
created_at: 2025-03-12T09:10:20Z
author_association: COLLABORATOR
diff_hunk: @@ -1046,3 +1049,15 @@ def deleted_ssp_operator_pod(admin_client, hco_namespace):
 @pytest.fixture(scope="class")
 def initiate_metric_value(request, prometheus):
     return get_metrics_value(prometheus=prometheus, metrics_name=request.param)
+
+
+@pytest.fixture(scope="class")
+def vnic_info_from_vm_and_vmi(request, vm_for_test):
+    vm_or_vmi = request.param
path: tests/observability/metrics/conftest.py
user: login: vsibirsk
body: used only once
created_at: 2025-03-12T09:26:48Z
author_association: COLLABORATOR
diff_hunk: @@ -1046,3 +1049,15 @@ def deleted_ssp_operator_pod(admin_client, hco_namespace):
 @pytest.fixture(scope="class")
 def initiate_metric_value(request, prometheus):
     return get_metrics_value(prometheus=prometheus, metrics_name=request.param)
+
+
+@pytest.fixture(scope="class")
path: tests/observability/metrics/conftest.py
user: login: hmeir
body: should it be function scoped?
created_at: 2025-03-12T10:21:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -492,3 +493,37 @@ def test_metric_kubevirt_vmsnapshot_persistentvolumeclaim_labels(
             ),
             expected_labels_and_values=snapshot_labels_for_testing,
         )
+
+
+@pytest.mark.parametrize(
+    "vm_for_test",
+    [
+        pytest.param(
+            "vnic-testing-vm",
+        ),
+    ],
+    indirect=True,
path: tests/observability/metrics/test_vms_metrics.py
user: login: Ahmad-Hafe
body: @OhadRevah I suggest to use 
running_metric_vm fixture instead of vm_for_test so then you wont need that extra parametrize for the class
created_at: 2025-03-12T18:19:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -492,3 +493,37 @@ def test_metric_kubevirt_vmsnapshot_persistentvolumeclaim_labels(
             ),
             expected_labels_and_values=snapshot_labels_for_testing,
         )
+
+
+@pytest.mark.parametrize(
+    "vm_for_test",
+    [
+        pytest.param(
+            "vnic-testing-vm",
+        ),
+    ],
+    indirect=True,
path: tests/observability/metrics/test_vms_metrics.py
user: login: vsibirsk
body: +1 to what @Ahmad-Hafe said
created_at: 2025-03-13T08:04:31Z
author_association: COLLABORATOR
diff_hunk: @@ -1230,3 +1232,23 @@ def validate_metric_value_with_round_down(
     except TimeoutExpiredError:
         LOGGER.info(f"Metric int value of: {metric_name} is: {sample}, expected value:{expected_value}")
         raise
+
+
+def binding_name_and_type_from_vm_or_vmi(vm_interface: dict[str, str]) -> dict[str, str]:
+    binding_name_and_type = None
+    for binding_name in ["masquerade", "bridge", "sriov"]:
+        if vm_interface.get(binding_name):
+            binding_name_and_type = {BINDING_NAME: binding_name, BINDING_TYPE: "core"}
+    assert binding_name_and_type, f"vm interface {vm_interface} has not valid binding name."
+    return binding_name_and_type
+
+
+def validate_vnic_info(prometheus: Prometheus, vnic_info_to_compare: dict[str, str], metric_name: str) -> None:
+    vnic_info_metric_result = prometheus.query_sampler(query=metric_name)[0].get("metric")
+    mismatch_vnic_info = {}
+    for info in vnic_info_to_compare:
path: tests/observability/metrics/utils.py
user: login: vsibirsk
body: ```suggestion
    for info,  expected_value in vnic_info_to_compare.items():
```
created_at: 2025-03-13T08:09:06Z
author_association: COLLABORATOR

PR comments:
user: login: dbasunag1
created_at: 2025-03-06T16:25:39Z
body: 
Report bugs in [Issues](https://github.com/myakove/github-webhook-server/issues)

The following are automatically added:
 * Add reviewers from OWNER file (in the root of the repository) under reviewers section.
 * Set PR size label.
 * New issue is created for the PR. (Closed when PR is merged/closed)
 * Run [pre-commit](https://pre-commit.ci/) if `.pre-commit-config.yaml` exists in the repo.

Available user actions:
 * To mark PR as WIP comment `/wip` to the PR, To remove it from the PR comment `/wip cancel` to the PR.
 * To block merging of PR comment `/hold`, To un-block merging of PR comment `/hold cancel`.
 * To mark PR as verified comment `/verified` to the PR, to un-verify comment `/verified cancel` to the PR.
        verified label removed on each new commit push.
 * To cherry pick a merged PR comment `/cherry-pick <target branch to cherry-pick to>` in the PR.
    * Multiple target branches can be cherry-picked, separated by spaces. (`/cherry-pick branch1 branch2`)
    * Cherry-pick will be started when PR is merged
 * To build and push container image command `/build-and-push-container` in the PR (tag will be the PR number).
    * You can add extra args to the Podman build command
        * Example: `/build-and-push-container --build-arg OPENSHIFT_PYTHON_WRAPPER_COMMIT=<commit_hash>`
 * To add a label by comment use `/<label name>`, to remove, use `/<label name> cancel`
 * To assign reviewers based on OWNERS file use `/assign-reviewers`
 * To check if PR can be merged use `/check-can-merge`
 * to assign reviewer to PR use `/assign-reviewer @<reviewer>`

<details>
<summary>Supported /retest check runs</summary>

 * `/retest tox`: Retest tox
 * `/retest build-container`: Retest build-container
 * `/retest all`: Retest all

</details>

<details>
<summary>Supported labels</summary>

 * hold
 * verified
 * wip
 * lgtm

</details>
    
author_association: NONE
user: login: OhadRevah
created_at: 2025-03-09T09:41:07Z
body: /build-and-push-container
author_association: CONTRIBUTOR
user: login: ghost
created_at: 2025-03-09T09:41:42Z
body: New container for quay.io/openshift-cnv/openshift-virtualization-tests:pr-437 published
author_association: NONE
user: login: OhadRevah
created_at: 2025-03-09T12:37:58Z
body: /verified
author_association: CONTRIBUTOR
user: login: OhadRevah
created_at: 2025-03-10T08:51:59Z
body: /build-and-push-container
author_association: CONTRIBUTOR
user: login: dbasunag1
created_at: 2025-03-10T08:52:37Z
body: New container for quay.io/openshift-cnv/openshift-virtualization-tests:pr-437 published
author_association: NONE
user: login: OhadRevah
created_at: 2025-03-10T12:33:57Z
body: /build-and-push-container
author_association: CONTRIBUTOR
user: login: ghost
created_at: 2025-03-10T12:34:33Z
body: New container for quay.io/openshift-cnv/openshift-virtualization-tests:pr-437 published
author_association: NONE
user: login: OhadRevah
created_at: 2025-03-10T15:38:15Z
body: /verified
author_association: CONTRIBUTOR
user: login: OhadRevah
created_at: 2025-03-11T11:13:46Z
body: /build-and-push-container
author_association: CONTRIBUTOR
user: login: ghost
created_at: 2025-03-11T11:14:23Z
body: New container for quay.io/openshift-cnv/openshift-virtualization-tests:pr-437 published
author_association: NONE
user: login: OhadRevah
created_at: 2025-03-11T12:51:07Z
body: /verified
author_association: CONTRIBUTOR
user: login: OhadRevah
created_at: 2025-03-11T15:24:39Z
body: /build-and-push-container
author_association: CONTRIBUTOR
user: login: ghost
created_at: 2025-03-11T15:25:17Z
body: New container for quay.io/openshift-cnv/openshift-virtualization-tests:pr-437 published
author_association: NONE
user: login: OhadRevah
created_at: 2025-03-12T11:05:19Z
body: /build-and-push-container
author_association: CONTRIBUTOR
user: login: dbasunag1
created_at: 2025-03-12T11:05:59Z
body: New container for quay.io/openshift-cnv/openshift-virtualization-tests:pr-437 published
author_association: NONE
user: login: OhadRevah
created_at: 2025-03-12T13:24:20Z
body: /verified
author_association: CONTRIBUTOR
user: login: OhadRevah
created_at: 2025-03-13T09:41:00Z
body: /build-and-push-container
author_association: CONTRIBUTOR
user: login: dbasunag1
created_at: 2025-03-13T09:41:40Z
body: New container for quay.io/openshift-cnv/openshift-virtualization-tests:pr-437 published
author_association: NONE
user: login: OhadRevah
created_at: 2025-03-13T10:49:59Z
body: /verified
author_association: CONTRIBUTOR
user: login: OhadRevah
created_at: 2025-03-16T14:43:08Z
body: /verified
author_association: CONTRIBUTOR
user: login: OhadRevah
created_at: 2025-03-17T10:45:01Z
body: /verified
author_association: CONTRIBUTOR
user: login: vsibirsk
created_at: 2025-03-17T10:47:38Z
body: /check-can-merge
author_association: COLLABORATOR
user: login: vsibirsk
created_at: 2025-03-17T10:49:52Z
body: /verified
author_association: COLLABORATOR
user: login: vsibirsk
created_at: 2025-03-17T10:50:20Z
body: /check-can-merge
author_association: COLLABORATOR
user: login: vsibirsk
created_at: 2025-03-17T10:51:54Z
body: /check-can-merge
author_association: COLLABORATOR
user: login: ghost
created_at: 2025-03-17T10:52:54Z
body: Successfully removed PR tag: quay.io/openshift-cnv/openshift-virtualization-tests:pr-437.
author_association: NONE
user: login: ghost
created_at: 2025-03-17T10:54:57Z
body: New container for quay.io/openshift-cnv/openshift-virtualization-tests:latest published
author_association: NONE