Skip to content

Commit 53a5999

Browse files
authored
Merge branch 'main' into testGuestLoadMetrics
2 parents 17b4212 + 08bb3bb commit 53a5999

12 files changed

Lines changed: 284 additions & 190 deletions

File tree

Dockerfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ ENV UV_NO_SYNC=1
3636

3737
WORKDIR ${TEST_DIR}
3838
ENV UV_CACHE_DIR=${TEST_DIR}/.cache
39+
ENV HOME=${TEST_DIR}
3940

4041
##TODO: We can remove wget, and use curl instead, this will require to change some tests
4142
RUN dnf update -y \
42-
&& dnf install -y procps-ng python3 bind-utils jq fwknop parallel wget clang cargo rsync openssl openssl-devel git\
43+
&& dnf install -y procps-ng python3 bind-utils jq fwknop parallel wget clang cargo rsync openssl openssl-devel git unzip\
4344
&& dnf clean all \
4445
&& rm -rf /var/cache/dnf \
4546
&& rm -rf /var/lib/dnf \
@@ -50,6 +51,9 @@ COPY --from=builder /usr/bin/which /usr/bin/which
5051
COPY --from=builder /usr/bin/sshpass /usr/bin/sshpass
5152
COPY --from=builder ${TEST_DIR}/ ${TEST_DIR}/
5253

54+
RUN curl -fsSL https://bws.bitwarden.com/install | sh \
55+
&& mkdir -p ${TEST_DIR}/.config/bws/state
56+
5357
RUN uv sync --locked \
5458
&& uv export --no-hashes \
5559
&& if [[ -n "${OPENSHIFT_PYTHON_WRAPPER_COMMIT}" ]]; then uv pip install git+https://github.com/RedHatQE/openshift-python-wrapper.git@$OPENSHIFT_PYTHON_WRAPPER_COMMIT; fi \

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ authors = [{ "name" = "openshift-virtualization-tests" }]
3838
dependencies = [
3939
"bcrypt>=4.2.0",
4040
"bitmath>=1.3.3.1",
41-
"bitwarden-sdk>=1.0.0",
4241
"bs4>=0.0.2",
4342
"click>=8.1.7",
4443
"colorlog>=6.9.0",

tests/network/connectivity/utils.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from collections import OrderedDict
22

3+
from tests.network.libs.ip import random_ipv4_address
34
from utilities.constants import IPV6_STR
45
from utilities.network import (
56
compose_cloud_init_data_dict,
@@ -31,7 +32,10 @@ def create_running_vm(
3132
node_selector=node_selector,
3233
cloud_init_data=compose_cloud_init_data_dict(
3334
network_data={
34-
"ethernets": {f"eth{i + 1}": {"addresses": [f"10.200.{i}.{end_ip_octet}/24"]} for i in range(0, 3)}
35+
"ethernets": {
36+
f"eth{i + 1}": {"addresses": [f"{random_ipv4_address(net_seed=i, host_address=end_ip_octet)}/24"]}
37+
for i in range(0, 3)
38+
}
3539
},
3640
ipv6_network_data=dual_stack_network_data,
3741
),

tests/network/kubemacpool/test_kubemacpool.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@
99

1010
@pytest.mark.s390x
1111
class TestKMPConnectivity:
12-
#: KMPTestConnectivity setup
13-
# ......... ..........
14-
# | |---eth0: : POD network :auto: eth0---| |
15-
# | |---eth1:10.200.1.1: Manual MAC from pool:10.200.1.2:eth1---| |
16-
# | VM-A |---eth2:10.200.2.1: Automatic MAC from pool:10.200.2.2:eth2---| VM-B |
17-
# | |---eth3:10.200.3.1: Manual MAC not from pool:10.200.3.2:eth3---| |
18-
# |.......|---eth4:10.200.4.1: Automatic mac tuning network :10.200.4.2:eth4---|........|
12+
# KMPTestConnectivity setup example
13+
# Third octet is random
14+
# ......... ..........
15+
# | |---eth0: : POD network :auto :eth0---| |
16+
# | |---eth1:172.16.37.1: Manual MAC from pool :172.16.37.2:eth1---| |
17+
# | VM-A |---eth2:172.16.24.1: Automatic MAC from pool :172.16.24.2:eth2---| VM-B |
18+
# | |---eth3:172.16.51.1: Manual MAC not from pool :172.16.51.2:eth3---| |
19+
# |.......|---eth4:172.16.65.1: Automatic mac tuning network :172.16.65.2:eth4---|........|
1920
@pytest.mark.post_upgrade
2021
@pytest.mark.polarion("CNV-2154")
2122
def test_manual_mac_from_pool(self, namespace, running_vm_a, running_vm_b):

tests/network/kubemacpool/utils.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from collections import namedtuple
33
from ipaddress import ip_interface
44

5+
from tests.network.libs.ip import random_ipv4_address
56
from utilities.network import cloud_init_network_data, get_vmi_mac_address_by_iface_name
67
from utilities.virt import (
78
VirtualMachineForTests,
@@ -26,17 +27,25 @@ def vm_network_config(mac_pool, all_nads, end_ip_octet, mac_uid):
2627
"""
2728
return {
2829
"eth1": IfaceTuple(
29-
ip_address=f"10.200.1.{end_ip_octet}",
30+
ip_address=random_ipv4_address(net_seed=0, host_address=end_ip_octet),
3031
mac_address=mac_pool.get_mac_from_pool(),
3132
name=all_nads[0],
3233
),
33-
"eth2": IfaceTuple(ip_address=f"10.200.2.{end_ip_octet}", mac_address="auto", name=all_nads[1]),
34+
"eth2": IfaceTuple(
35+
ip_address=random_ipv4_address(net_seed=1, host_address=end_ip_octet),
36+
mac_address="auto",
37+
name=all_nads[1],
38+
),
3439
"eth3": IfaceTuple(
35-
ip_address=f"10.200.3.{end_ip_octet}",
40+
ip_address=random_ipv4_address(net_seed=2, host_address=end_ip_octet),
3641
mac_address=f"02:0{mac_uid}:00:00:00:00",
3742
name=all_nads[2],
3843
),
39-
"eth4": IfaceTuple(ip_address=f"10.200.4.{end_ip_octet}", mac_address="auto", name=all_nads[3]),
44+
"eth4": IfaceTuple(
45+
ip_address=random_ipv4_address(net_seed=3, host_address=end_ip_octet),
46+
mac_address="auto",
47+
name=all_nads[3],
48+
),
4049
}
4150

4251

tests/network/libs/ip.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import random
2+
from functools import cache
3+
from typing import Final
4+
5+
_MAX_NUM_OF_RANDOM_OCTETS_PER_SESSION: Final[int] = 4
6+
_IPV4_ADDRESS_SUBNET_PREFIX_VMI: Final[str] = "172.16"
7+
8+
9+
def random_ipv4_address(net_seed: int, host_address: int) -> str:
10+
"""Construct a random IPv4 address using a cached list of random third octets.
11+
12+
Uses a pre-defined network address, a cached random third octet and the given
13+
host address to generate deterministic yet randomized IPv4 addresses.
14+
15+
Args:
16+
net_seed (int): The index used to select a random third octet from the cached list.
17+
host_address (int): The last (fourth) octet of the IPv4 address.
18+
19+
Returns:
20+
str: A string representing a randomized IPv4 address.
21+
"""
22+
third_octets = _random_octets(count=_MAX_NUM_OF_RANDOM_OCTETS_PER_SESSION)
23+
return f"{_IPV4_ADDRESS_SUBNET_PREFIX_VMI}.{third_octets[net_seed]}.{host_address}"
24+
25+
26+
@cache
27+
def _random_octets(count: int) -> list[int]:
28+
"""Generate a list of random IPv4 octet values.
29+
30+
Randomly selects unique integers between 1 and 253 (inclusive) to be used
31+
as the third octet in an IPv4 address.
32+
33+
Args:
34+
count (int): The number of random octet values to generate.
35+
36+
Returns:
37+
list[int]: A list of unique random integers representing octet values.
38+
"""
39+
return random.sample(range(1, 254), count)

tests/network/localnet/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def vm_ovs_bridge_localnet_link_down(
209209
) -> Generator[BaseVirtualMachine]:
210210
with localnet_vm(
211211
namespace=namespace_localnet_1.name,
212-
name="localnet-ovs-vm1",
212+
name="localnet-ovs-link-down-vm",
213213
physical_network_name=cudn_localnet_ovs_bridge.name,
214214
spec_logical_network=LOCALNET_OVS_BRIDGE_NETWORK,
215215
cidr=next(ipv4_localnet_address_pool),

tests/network/localnet/liblocalnet.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import contextlib
2+
import logging
23
from typing import Generator
34

5+
from kubernetes.client import ApiException
6+
47
from libs.net.traffic_generator import Client, Server
58
from libs.net.vmspec import IP_ADDRESS, add_network_interface, add_volume_disk, lookup_iface_status
69
from libs.vm.affinity import new_pod_anti_affinity
@@ -17,11 +20,17 @@
1720
LINK_STATE_UP = "up"
1821
LINK_STATE_DOWN = "down"
1922
_IPERF_SERVER_PORT = 5201
23+
LOGGER = logging.getLogger(__name__)
2024

2125

2226
def run_vms(vms: tuple[BaseVirtualMachine, ...]) -> tuple[BaseVirtualMachine, ...]:
2327
for vm in vms:
24-
vm.start() # type: ignore[no-untyped-call]
28+
try:
29+
vm.start() # type: ignore[no-untyped-call]
30+
except ApiException as vm_exception:
31+
if "VM is already running" in vm_exception.body:
32+
LOGGER.warning(f"VM {vm.name} is already running")
33+
continue
2534
for vm in vms:
2635
vm.wait_for_ready_status(status=True) # type: ignore[no-untyped-call]
2736
vm.wait_for_agent_connected()

tests/network/nmstate/test_connectivity_after_nmstate_changes.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from ocp_resources.resource import ResourceEditor
66
from timeout_sampler import TimeoutSampler
77

8+
from tests.network.libs.ip import random_ipv4_address
89
from tests.network.utils import (
910
assert_nncp_successfully_configured,
1011
assert_ssh_alive,
@@ -94,7 +95,7 @@ def nmstate_linux_bridge_attached_vma(
9495
networks[nmstate_linux_nad.name] = nmstate_linux_nad.name
9596
network_data_data = {
9697
"ethernets": {
97-
"eth1": {"addresses": ["10.200.0.1/24"]},
98+
"eth1": {"addresses": [f"{random_ipv4_address(net_seed=0, host_address=1)}/24"]},
9899
}
99100
}
100101

@@ -128,7 +129,7 @@ def nmstate_linux_bridge_attached_vmb(
128129
networks[nmstate_linux_nad.name] = nmstate_linux_nad.name
129130
network_data_data = {
130131
"ethernets": {
131-
"eth1": {"addresses": ["10.200.0.2/24"]},
132+
"eth1": {"addresses": [f"{random_ipv4_address(net_seed=0, host_address=2)}/24"]},
132133
}
133134
}
134135

utilities/bitwarden.py

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,82 @@
11
import json
22
import logging
33
import os
4-
from functools import lru_cache
4+
from functools import cache
5+
from typing import Any
56

6-
from bitwarden_sdk import BitwardenClient
7+
from pyhelper_utils.shell import run_command
78

89
from utilities.exceptions import MissingEnvironmentVariableError
910

1011
LOGGER = logging.getLogger(__name__)
11-
# Bitwarden SDK: https://github.com/bitwarden/sdk/blob/main/languages/python/bitwarden_sdk/bitwarden_client.py
1212

1313

14-
def get_bitwarden_secrets_client():
15-
"""
16-
Creates a BitwardenClient instance, logs in using ACCESS_TOKEN environment variable and bitwarden AuthClient
17-
instance, and returns SecretsClient. To use bitwarden secret manager ACCESS_TOKEN and ORGANIZATION_ID environment
18-
variables must be set
14+
def _run_bws_command(args: list[str]) -> Any:
15+
"""Run bws CLI command and return parsed JSON output.
16+
17+
Args:
18+
args: Command arguments to pass to bws (e.g., ['secret', 'list'])
1919
2020
Returns:
21-
SecretsClient: Returns SecretsClient instance to be used for secret manager calls
21+
Any: Parsed JSON response from bws CLI (can be list or dict depending on command)
22+
23+
Raises:
24+
MissingEnvironmentVariableError: If ACCESS_TOKEN not set
2225
"""
23-
if not (os.getenv("ACCESS_TOKEN") and os.getenv("ORGANIZATION_ID")):
24-
raise MissingEnvironmentVariableError(
25-
"Bitwarden client needs ORGANIZATION_ID and ACCESS_TOKEN environment variable set up"
26-
)
27-
bitwarden_client = BitwardenClient()
28-
bitwarden_client.auth().login_access_token(access_token=os.getenv("ACCESS_TOKEN"))
29-
return bitwarden_client.secrets()
26+
access_token = os.getenv("ACCESS_TOKEN")
3027

28+
if not access_token:
29+
raise MissingEnvironmentVariableError("Bitwarden client needs ACCESS_TOKEN environment variable set up")
3130

32-
@lru_cache
33-
def get_all_cnv_tests_secrets(bitwarden_secrets_client):
34-
"""
35-
Using Bitwarden SecretsClient, gets a list of all cnv-secrets saved in bitwarden secret manager (associated with
36-
a specific organization id). ORGANIZATION_ID is expected to set via environment variable.
31+
_, stdout, _ = run_command(
32+
command=["bws", "--access-token", access_token] + args,
33+
capture_output=True,
34+
check=True,
35+
hide_log_command=True,
36+
)
3737

38-
Args:
39-
bitwarden_secrets_client (SecretsClient): Bitwarden SecretsClient instance
38+
return json.loads(stdout)
39+
40+
41+
@cache
42+
def get_all_cnv_tests_secrets() -> dict[str, str]:
43+
"""Gets a list of all cnv-secrets saved in Bitwarden Secret Manager.
44+
45+
Uses bws CLI to list all secrets associated with the organization.
46+
ACCESS_TOKEN environment variable must be set.
4047
4148
Returns:
42-
dict: dictionary of secret name and secret uuid associated with the organization
49+
dict[str, str]: Dictionary mapping secret name to secret UUID
4350
"""
44-
secrets = bitwarden_secrets_client.list(organization_id=os.getenv("ORGANIZATION_ID")).data.data
51+
data = _run_bws_command(args=["secret", "list"])
52+
4553
LOGGER.info(f"Cache info stats for pulling secrets: {get_all_cnv_tests_secrets.cache_info()}")
46-
return {secret.key: secret.id for secret in secrets}
4754

55+
return {secret["key"]: secret["id"] for secret in data}
4856

49-
@lru_cache
50-
def get_cnv_tests_secret_by_name(secret_name):
51-
"""
52-
Pull a specific secret from bitwarden secret manager by name
57+
58+
@cache
59+
def get_cnv_tests_secret_by_name(secret_name: str) -> dict[str, Any]:
60+
"""Pull a specific secret from Bitwarden Secret Manager by name.
5361
5462
Args:
55-
secret_name (str): Bitwarden secret manager secret name
63+
secret_name: Bitwarden Secret Manager secret name
5664
5765
Returns:
58-
dict: value of the saved secret
66+
dict[str, Any]: Value of the saved secret (parsed from JSON)
67+
68+
Raises:
69+
ValueError: If secret is not found
5970
"""
60-
bitwarden_secrets_client = get_bitwarden_secrets_client()
61-
secrets = get_all_cnv_tests_secrets(bitwarden_secrets_client=bitwarden_secrets_client)
62-
secret_dict = None
63-
for secret_key, secret_value in secrets.items():
64-
if secret_key == secret_name:
65-
secret_dict = json.loads(bitwarden_secrets_client.get(id=secret_value).data.value)
66-
break
71+
secrets = get_all_cnv_tests_secrets()
72+
73+
secret_id = secrets.get(secret_name)
74+
if not secret_id:
75+
raise ValueError(f"Secret '{secret_name}' not found in Bitwarden")
76+
77+
secret_data = _run_bws_command(args=["secret", "get", secret_id])
78+
secret_value = secret_data.get("value", "")
79+
80+
secret_dict = json.loads(secret_value)
6781
LOGGER.info(f"Cache info stats for getting specific secret: {get_cnv_tests_secret_by_name.cache_info()}")
68-
assert secret_dict, f"secret {secret_name} is either not found or does not have valid values."
6982
return secret_dict

0 commit comments

Comments
 (0)