Skip to content

Commit 8a482ec

Browse files
committed
fixup! feat(oracle): allow for specifying network configuration
1 parent 549572b commit 8a482ec

File tree

3 files changed

+261
-0
lines changed

3 files changed

+261
-0
lines changed

pycloudlib/oci/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818
OCI_SDK_NULL = "<null>"
1919
ORACLE_IMDS_NULL = "\u003cnull\u003e"
2020

21+
2122
def _oci_sdk_string_is_truthy(value: Optional[str]) -> bool:
2223
"""Check if value returned by OCI SDK is truthy."""
2324
return value not in (OCI_SDK_NULL, ORACLE_IMDS_NULL, None, "")
2425

26+
2527
def wait_till_ready(
2628
func,
2729
current_data,
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
"""
2+
Integration test that exercise functionality specific to Oracle's launch function.
3+
4+
The basic lifecycle stuff is already tested in `tests/integration_tests/test_public_api.py`, but
5+
these tests go beyond the standard tests that exercise the base cloud agnostic functionality.
6+
"""
7+
8+
import json
9+
import logging
10+
11+
import pytest
12+
13+
from pycloudlib.oci.cloud import OCI
14+
from pycloudlib.types import NetworkingConfig, NetworkingType
15+
16+
logger = logging.getLogger(__name__)
17+
18+
19+
# create fixture that provides the oracle cloud object
20+
@pytest.fixture(scope="module")
21+
def oracle_cloud():
22+
"""Provide an OCI cloud instance for tests with automatic cleanup.
23+
24+
Returns:
25+
An OCI cloud instance configured for testing.
26+
"""
27+
# make sure region, AD, and compartment_id are set in your pycloudlib.toml config file
28+
# use context manager - instances will be deleted automatically after the test
29+
with OCI(
30+
tag="oracle-integrations-test-launch",
31+
) as oracle_cloud:
32+
yield oracle_cloud
33+
34+
35+
class TestOracleLaunch:
36+
"""
37+
Test Oracle Cloud Infrastructure instance launch functionality.
38+
39+
This class contains tests specific to the OCI launch method,
40+
including various network configurations.
41+
"""
42+
43+
@pytest.mark.parametrize(
44+
("instance_type",),
45+
[
46+
pytest.param(
47+
"VM.Standard2.1",
48+
id="VM",
49+
),
50+
pytest.param(
51+
"BM.Optimized3.36",
52+
id="BM",
53+
),
54+
],
55+
)
56+
@pytest.mark.parametrize(
57+
(
58+
"primary_private",
59+
"primary_networking_type",
60+
"secondary_private",
61+
"secondary_networking_type",
62+
),
63+
[
64+
# both public ipv4
65+
pytest.param(
66+
False,
67+
NetworkingType.IPV4,
68+
True,
69+
NetworkingType.IPV4,
70+
id="both_public_ipv4",
71+
),
72+
# both public ipv6
73+
pytest.param(
74+
False,
75+
NetworkingType.IPV6,
76+
True,
77+
NetworkingType.IPV6,
78+
id="both_public_ipv6",
79+
),
80+
# primary public dual stack, secondary private ipv4
81+
pytest.param(
82+
False,
83+
NetworkingType.DUAL_STACK,
84+
True,
85+
NetworkingType.DUAL_STACK,
86+
id="public_dual_stack_private_dual_stack",
87+
),
88+
],
89+
)
90+
def test_launch_with_networking_configs(
91+
self,
92+
oracle_cloud: OCI,
93+
primary_private: bool,
94+
primary_networking_type: NetworkingType,
95+
secondary_private: bool,
96+
secondary_networking_type: NetworkingType,
97+
instance_type: str,
98+
):
99+
"""Test OCI instance launch with various networking configurations.
100+
101+
This test verifies that instances can be launched with different
102+
combinations of networking configurations (IPv4, IPv6, dual-stack)
103+
for both primary and secondary network interfaces.
104+
105+
Args:
106+
oracle_cloud (OCI): The OCI cloud fixture.
107+
primary_private (bool): Whether primary NIC should be private.
108+
primary_networking_type (NetworkingType): Network type for primary NIC.
109+
secondary_private (bool): Whether secondary NIC should be private.
110+
secondary_networking_type (NetworkingType): Network type for secondary NIC.
111+
instance_type (str): OCI instance type to launch.
112+
113+
Test Steps:
114+
1. Launch an instance with the specified primary networking configuration and
115+
instance type
116+
2. Add a secondary network interface with the specified secondary networking
117+
configuration
118+
3. Restart the instance to apply the changes (As of 20250226 cloud-init does not support
119+
hotplugging nics on Oracle)
120+
4. Verify that the instance has the expected number of VNICs in IMDS
121+
"""
122+
primary_networking_config = NetworkingConfig(
123+
private=primary_private,
124+
networking_type=primary_networking_type,
125+
)
126+
127+
logger.info("Launching instance...")
128+
instance = oracle_cloud.launch(
129+
image_id="ocid1.image.oc1.iad.aaaaaaaasukfowgzghuwrljl4ohlpv3uadhm5sn5dderkhhyymelebrzoima",
130+
primary_network_config=primary_networking_config,
131+
instance_type=instance_type,
132+
)
133+
logger.info("Instance launched. Waiting for instance to be ready...")
134+
instance.wait()
135+
logger.info("Instance is ready!")
136+
assert instance.execute("true").ok
137+
138+
if primary_networking_config.networking_type == NetworkingType.IPV6:
139+
imds_vnics_url = "curl http://[fd00:c1::a9fe:a9fe]/opc/v1/vnics"
140+
else:
141+
imds_vnics_url = "curl http://169.254.169.254/opc/v1/vnics"
142+
143+
secondary_networking_config = NetworkingConfig(
144+
private=secondary_private,
145+
networking_type=secondary_networking_type,
146+
)
147+
instance.add_network_interface(
148+
nic_index=(1 if instance_type == "BM.Optimized3.36" else 0),
149+
networking_config=secondary_networking_config,
150+
)
151+
152+
# run cloud-init clean and restart instance now that secondary NIC has been added
153+
logger.info("Running cloud-init clean and restarting instance...")
154+
instance.execute("cloud-init clean", use_sudo=True)
155+
instance.restart(wait=True)
156+
157+
logger.info("Getting VNIC data from IMDS at '%s'...", imds_vnics_url)
158+
imds_response_2 = instance.execute(f"curl -s {imds_vnics_url}").stdout
159+
vnic_data_2 = json.loads(imds_response_2)
160+
logger.info("VNIC data from IMDS after adding secondary NIC: %s", imds_response_2)
161+
assert len(vnic_data_2) == 2, "Expected IMDS to return 2 VNICs after adding secondary NIC"
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"""Integration tests for Oracle's utility functions."""
2+
3+
import logging
4+
5+
import pytest
6+
7+
from pycloudlib.oci.cloud import OCI
8+
from pycloudlib.types import NetworkingConfig, NetworkingType
9+
10+
logger = logging.getLogger(__name__)
11+
12+
13+
@pytest.fixture
14+
def oci_cloud():
15+
"""Fixture to create an OCI cloud instance."""
16+
with OCI(
17+
tag="oracle-integrations-test-utils",
18+
vcn_name="ipv6-vcn",
19+
region="us-ashburn-1",
20+
compartment_id="ocid1.compartment.oc1..aaaaaaaayyvhlkxdjkhzu56is7qenv35h4jfh26oconxsro4qr2qx6ezgbpq",
21+
availability_domain="qIZq:US-ASHBURN-AD-2",
22+
) as oracle_cloud:
23+
yield oracle_cloud
24+
25+
26+
# @a-dubs - These are pre-existing subnets that I have created in my Oracle Cloud account.
27+
# this is not immediately reproducible by others, but all they need to do is create 3 subnets
28+
# that match the below configurations and update the following variables with the new subnet ids.
29+
# This is the only way I could feel confident that my subnet selection logic is working with the
30+
# new networking configuration options as expected.
31+
IPV6_PUBLIC_SUBNET_ID = (
32+
"ocid1.subnet.oc1.iad.aaaaaaaaofjoplcmjw4jtr22654bzkpo7od3jzij5z4phrhuhgtwrldvxihq"
33+
)
34+
DUAL_STACK_PUBLIC_SUBNET_ID = (
35+
"ocid1.subnet.oc1.iad.aaaaaaaaz3ih5vu6z4bypvkte633vk6my4umrzhdunluikxuj2hvzbruy73a"
36+
)
37+
DUAL_STACK_PRIVATE_SUBNET_ID = (
38+
"ocid1.subnet.oc1.iad.aaaaaaaaahhibjqn6x4re7t4ojegavini7wjxcrh3whkyq6ix5vfdx6om5aa"
39+
)
40+
41+
42+
@pytest.mark.parametrize(
43+
["networking_type", "private", "expected_subnet_id"],
44+
[
45+
pytest.param(
46+
NetworkingType.IPV6,
47+
False,
48+
IPV6_PUBLIC_SUBNET_ID,
49+
id="ipv6_public",
50+
),
51+
pytest.param(
52+
NetworkingType.DUAL_STACK,
53+
True,
54+
DUAL_STACK_PRIVATE_SUBNET_ID,
55+
id="dual_stack_private",
56+
),
57+
pytest.param(
58+
NetworkingType.DUAL_STACK,
59+
False,
60+
DUAL_STACK_PUBLIC_SUBNET_ID,
61+
id="dual_stack_public",
62+
),
63+
pytest.param(
64+
NetworkingType.IPV4,
65+
False,
66+
DUAL_STACK_PUBLIC_SUBNET_ID,
67+
id="ipv4_public",
68+
),
69+
pytest.param(
70+
NetworkingType.IPV4,
71+
True,
72+
DUAL_STACK_PRIVATE_SUBNET_ID,
73+
id="ipv4_private",
74+
),
75+
],
76+
)
77+
def test_oci_subnet_finding(oci_cloud: OCI, networking_type, private, expected_subnet_id):
78+
"""
79+
Test finding a subnet in OCI.
80+
81+
We are validating that the correct subnet is found based on the type of networking and whether
82+
the instance should be publicly accessible or not.
83+
"""
84+
network_config: NetworkingConfig = NetworkingConfig(
85+
networking_type=networking_type,
86+
private=private,
87+
)
88+
subnet_id = oci_cloud.find_compatible_subnet(
89+
networking_config=network_config,
90+
)
91+
92+
logger.info(
93+
f"Found subnet ID: {subnet_id} for networking type: {networking_type} "
94+
f"and privacy: {private}"
95+
)
96+
assert subnet_id == expected_subnet_id, (
97+
f"Expected subnet ID: {expected_subnet_id} but got: {subnet_id}",
98+
)

0 commit comments

Comments
 (0)