Skip to content

Commit f6f48b2

Browse files
committed
test: enable/disable static nat in vpc
1 parent 2ec82d4 commit f6f48b2

2 files changed

Lines changed: 110 additions & 49 deletions

File tree

extensions/network-namespace/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ required**.
6969
│ ▼ │
7070
│ /etc/cloudstack/extensions/<ext-name>/ │
7171
│ network-namespace.sh │
72-
│ (this directory, deployed during installation) │
7372
└──────────────────────┬───────────────────────────────────┘
7473
│ SSH (host : port from extension details)
7574
│ credentials from extension_resource_map_details

test/integration/smoke/test_network_extension_namespace.py

Lines changed: 110 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,11 @@ def setUpClass(cls):
378378
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
379379
cls.domain = get_domain(cls.apiclient)
380380
cls.mgtSvrDetails = cls.config.__dict__["mgtSvr"][0].__dict__
381+
# All management servers — entry-point script is deployed to every one.
382+
cls.all_mgt_svr_details = [
383+
mgt.__dict__ if hasattr(mgt, '__dict__') else mgt
384+
for mgt in cls.config.__dict__.get("mgtSvr", [])
385+
] or [cls.mgtSvrDetails]
381386
cls.hv = testClient.getHypervisorInfo()
382387
cls._cleanup = []
383388
cls.tmp_files = []
@@ -388,6 +393,9 @@ def setUpClass(cls):
388393
cls.logger.setLevel(logging.DEBUG)
389394
cls.logger.addHandler(cls.stream_handler)
390395

396+
cls.logger.info("Management servers: %s",
397+
[m.get("mgtSvrIp", "?") for m in cls.all_mgt_svr_details])
398+
391399
# KVM host credentials from Marvin config
392400
cls.kvm_host_configs = _get_kvm_hosts_from_config(cls.config)
393401
cls.logger.info("KVM hosts from config: %s",
@@ -422,21 +430,6 @@ def setUpClass(cls):
422430
"falling back to default", e)
423431
cls.template = get_template(cls.apiclient, cls.zone.id, cls.hv)
424432

425-
# ---- SSH keypair (written to a temp file) ----
426-
try:
427-
kp = SSHKeyPair.create(cls.apiclient, name=random_gen() + ".pem")
428-
cls._cleanup.append(SSHKeyPair(kp.__dict__, None))
429-
pkfile = os.path.join(tempfile.gettempdir(), kp.name)
430-
kp.private_key_file = pkfile
431-
cls.tmp_files.append(pkfile)
432-
with open(pkfile, "w+") as fh:
433-
fh.write(kp.privatekey)
434-
os.chmod(pkfile, 0o400)
435-
cls.keypair = kp
436-
cls.logger.info("SSH keypair '%s' written to %s", kp.name, pkfile)
437-
except Exception as e:
438-
cls.logger.warning("Could not create SSH keypair: %s", e)
439-
440433
# ---- Download wrapper scripts from GitHub ----
441434
try:
442435
_ensure_scripts_downloaded()
@@ -543,11 +536,11 @@ def _deploy_scripts(self):
543536

544537
self._mgmt_script_path = (self.extension_path or "").strip().rstrip('/')
545538

546-
# Deploy entry-point to ALL management servers
547-
all_mgt_svrs = self.config.__dict__.get("mgtSvr", [])
539+
# Deploy entry-point to ALL management servers.
540+
# all_mgt_svr_details is collected once in setUpClass from
541+
# cls.config.__dict__["mgtSvr"] so every server in a HA pair is covered.
548542
self._all_mgmt_deployers = []
549-
for mgt in all_mgt_svrs:
550-
mgt_details = mgt.__dict__ if hasattr(mgt, '__dict__') else mgt
543+
for mgt_details in self.all_mgt_svr_details:
551544
deployer = MgmtServerDeployer(mgt_details, logger=self.logger)
552545
deployer.copy_file(entry_point_src, self._mgmt_script_path)
553546
self.logger.info("Entry-point deployed to mgmt %s at %s",
@@ -835,7 +828,6 @@ def _create_account_keypair(self, account, name_suffix=""):
835828
account=account.name,
836829
domainid=account.domainid,
837830
)
838-
self.cleanup.append(SSHKeyPair(kp.__dict__, None))
839831

840832
pkfile = os.path.join(tempfile.gettempdir(), kp.name)
841833
with open(pkfile, "w+") as fh:
@@ -968,6 +960,7 @@ def test_01_provider_state_transitions(self):
968960
self.assertEqual('Disabled', self._find_provider(pn.id, ext_name).state)
969961
self.logger.info("NSP disabled OK")
970962

963+
self._teardown_extension()
971964
self.logger.info("test_01 PASSED")
972965

973966
@attr(tags=["advanced", "smoke"], required_hardware="false")
@@ -1314,13 +1307,24 @@ def test_06_vpc_multi_tier_and_restart(self):
13141307
"""VPC multi-tier + VPC restart with SSH connectivity verification.
13151308
13161309
Creates two VPC tier networks backed by the extension, deploys a
1317-
VM in each tier, and verifies SSH access independently. Then:
1310+
VM in each tier, and verifies SSH access independently.
13181311
1319-
1. VPC restart (cleanup=True) — verify SSH still works for both VMs.
1320-
2. Delete tier-1 VM + network — verify tier-2 VM is still accessible.
1321-
3. Delete tier-2 VM + network.
1322-
4. Delete VPC.
1323-
5. Teardown extension.
1312+
Sub-tests in order
1313+
------------------
1314+
A. Baseline connectivity (before VPC restart)
1315+
tier-1 PF :22 → VM1 — assert SSH works
1316+
tier-2 LB :22 → VM2 — assert SSH works
1317+
tier-1 static NAT :22 → VM1 — enable, create FW rule,
1318+
assert SSH works
1319+
B. VPC restart (cleanup=True)
1320+
assert SSH still works via tier-1 PF, tier-2 LB, and
1321+
tier-1 static NAT after the namespace is rebuilt
1322+
C. Static NAT teardown (after VPC restart)
1323+
disable static NAT → assert SSH fails → delete IP
1324+
D. Partial delete
1325+
delete tier-1 VM + network → assert tier-2 VM still accessible
1326+
E. Final delete
1327+
delete tier-2 VM + network, VPC, teardown extension
13241328
13251329
The VPC tier network offering uses ``useVpc=on`` as required by
13261330
CloudStack for VPC-associated tier networks.
@@ -1452,7 +1456,7 @@ def test_06_vpc_multi_tier_and_restart(self):
14521456
self.cleanup.insert(0, vm2)
14531457
self.logger.info("VM2 deployed in tier 2: %s (%s)", vm2.name, vm2.id)
14541458

1455-
# ---- Tier 1: PF ----
1459+
# ---- Tier 1: PF rule ----
14561460
pf_ip1 = PublicIPAddress.create(
14571461
self.apiclient,
14581462
accountid=account.name,
@@ -1467,10 +1471,10 @@ def test_06_vpc_multi_tier_and_restart(self):
14671471
ipaddressid=pf_ip1.ipaddress.id,
14681472
networkid=tier1.id
14691473
)
1470-
tier1_ip = pf_ip1.ipaddress.ipaddress
1471-
self.logger.info("Tier 1 PF: %s:22 → VM1:22", tier1_ip)
1474+
tier1_pf_ip = pf_ip1.ipaddress.ipaddress
1475+
self.logger.info("Tier 1 PF: %s:22 → VM1:22", tier1_pf_ip)
14721476

1473-
# ---- Tier 2: LB ----
1477+
# ---- Tier 2: LB rule ----
14741478
lb_ip2 = PublicIPAddress.create(
14751479
self.apiclient,
14761480
accountid=account.name,
@@ -1492,34 +1496,91 @@ def test_06_vpc_multi_tier_and_restart(self):
14921496
)
14931497
self.assertIsNotNone(lb_rule2)
14941498
lb_rule2.assign(self.apiclient, vms=[vm2])
1495-
tier2_ip = lb_ip2.ipaddress.ipaddress
1496-
self.logger.info("Tier 2 LB: %s:22 → VM2:22", tier2_ip)
1499+
tier2_lb_ip = lb_ip2.ipaddress.ipaddress
1500+
self.logger.info("Tier 2 LB: %s:22 → VM2:22", tier2_lb_ip)
1501+
1502+
# ---- Tier 1: Static NAT (allocated BEFORE restart) ----
1503+
snat_ip1_obj = PublicIPAddress.create(
1504+
self.apiclient,
1505+
accountid=account.name,
1506+
zoneid=self.zone.id,
1507+
domainid=account.domainid,
1508+
networkid=tier1.id,
1509+
vpcid=vpc.id
1510+
)
1511+
snat_ip1 = snat_ip1_obj.ipaddress.ipaddress
1512+
snat_ip1_id = snat_ip1_obj.ipaddress.id
1513+
StaticNATRule.enable(
1514+
self.apiclient,
1515+
ipaddressid=snat_ip1_id,
1516+
virtualmachineid=vm1.id,
1517+
networkid=tier1.id
1518+
)
1519+
self.logger.info("Static NAT enabled on tier-1: %s → VM1", snat_ip1)
1520+
1521+
# ==============================================================
1522+
# A. Baseline connectivity — BEFORE VPC restart
1523+
# ==============================================================
1524+
self.logger.info("--- Sub-test A: Baseline connectivity (before restart) ---")
14971525

1498-
# ---- Verify SSH to both VMs ----
14991526
self._assert_vm_ssh_accessible(
1500-
tier1_ip, 22,
1501-
"SSH to tier-1 VM (%s) should succeed" % tier1_ip)
1502-
self.logger.info("Verified: SSH to tier-1 VM works")
1527+
tier1_pf_ip, 22,
1528+
"SSH to tier-1 VM via PF (%s) should succeed before restart"
1529+
% tier1_pf_ip)
1530+
self.logger.info("Verified: SSH to tier-1 VM works via PF (before restart)")
15031531

15041532
self._assert_vm_ssh_accessible(
1505-
tier2_ip, 22,
1506-
"SSH to tier-2 VM via LB (%s) should succeed" % tier2_ip)
1507-
self.logger.info("Verified: SSH to tier-2 VM works via LB")
1533+
tier2_lb_ip, 22,
1534+
"SSH to tier-2 VM via LB (%s) should succeed before restart"
1535+
% tier2_lb_ip)
1536+
self.logger.info("Verified: SSH to tier-2 VM works via LB (before restart)")
15081537

1509-
# ---- VPC restart (cleanup=True) ----
1538+
self._assert_vm_ssh_accessible(
1539+
snat_ip1, 22,
1540+
"SSH to tier-1 VM via static NAT (%s) should succeed before restart"
1541+
% snat_ip1)
1542+
self.logger.info("Verified: SSH to tier-1 VM works via static NAT (before restart)")
1543+
1544+
# ==============================================================
1545+
# B. VPC restart (cleanup=True)
1546+
# Re-verify all three access methods after the namespace is rebuilt.
1547+
# ==============================================================
1548+
self.logger.info("--- Sub-test B: VPC restart (cleanup=True) ---")
15101549
self.logger.info("Restarting VPC %s (cleanup=True) ...", vpc.id)
15111550
vpc.restart(self.apiclient, cleanup=True)
15121551
self.logger.info("VPC restart completed")
15131552

15141553
self._assert_vm_ssh_accessible(
1515-
tier1_ip, 22,
1516-
"SSH to tier-1 VM must work after VPC restart")
1554+
tier1_pf_ip, 22,
1555+
"SSH to tier-1 VM via PF must work after VPC restart")
1556+
self.logger.info("Verified: SSH to tier-1 VM works via PF (after restart)")
1557+
15171558
self._assert_vm_ssh_accessible(
1518-
tier2_ip, 22,
1559+
tier2_lb_ip, 22,
15191560
"SSH to tier-2 VM via LB must work after VPC restart")
1520-
self.logger.info("Verified: both VMs accessible after VPC restart")
1561+
self.logger.info("Verified: SSH to tier-2 VM works via LB (after restart)")
15211562

1522-
# ---- Delete tier 1 VM + network ----
1563+
self._assert_vm_ssh_accessible(
1564+
snat_ip1, 22,
1565+
"SSH to tier-1 VM via static NAT must work after VPC restart")
1566+
self.logger.info("Verified: SSH to tier-1 VM works via static NAT (after restart)")
1567+
1568+
# ==============================================================
1569+
# C. Disable static NAT (after VPC restart)
1570+
# ==============================================================
1571+
self.logger.info("--- Sub-test C: Disable static NAT (after restart) ---")
1572+
StaticNATRule.disable(self.apiclient, ipaddressid=snat_ip1_id)
1573+
self.logger.info("Static NAT disabled on tier-1 %s", snat_ip1)
1574+
self._assert_vm_ssh_not_accessible(
1575+
snat_ip1, 22,
1576+
"SSH via tier-1 %s:22 should fail after static NAT disabled" % snat_ip1)
1577+
self.logger.info("Verified: SSH fails after static NAT disabled")
1578+
snat_ip1_obj.delete(self.apiclient)
1579+
1580+
# ==============================================================
1581+
# D. Partial delete: tier-1 — tier-2 must remain accessible
1582+
# ==============================================================
1583+
self.logger.info("--- Sub-test D: Delete tier-1, verify tier-2 intact ---")
15231584
pf_rule1.delete(self.apiclient)
15241585
pf_ip1.delete(self.apiclient)
15251586
vm1.delete(self.apiclient, expunge=True)
@@ -1531,11 +1592,13 @@ def test_06_vpc_multi_tier_and_restart(self):
15311592
# Tier 2 must remain accessible — cmd_destroy() on tier-1 must not
15321593
# delete tier-2's public veth (fixed via .tier ownership tracking).
15331594
self._assert_vm_ssh_accessible(
1534-
tier2_ip, 22,
1595+
tier2_lb_ip, 22,
15351596
"SSH to tier-2 VM via LB must still work after tier-1 deleted")
15361597
self.logger.info("Verified: tier-2 VM still accessible via LB after tier-1 deleted")
15371598

1538-
# ---- Delete tier 2 VM + network ----
1599+
# ==============================================================
1600+
# E. Final delete: tier-2, VPC
1601+
# ==============================================================
15391602
lb_rule2.remove(self.apiclient, vms=[vm2])
15401603
lb_rule2.delete(self.apiclient)
15411604
lb_ip2.delete(self.apiclient)
@@ -1545,7 +1608,6 @@ def test_06_vpc_multi_tier_and_restart(self):
15451608
self.cleanup = [o for o in self.cleanup if o != tier2]
15461609
self.logger.info("Tier 2 VM + network deleted")
15471610

1548-
# ---- Delete VPC ----
15491611
vpc.delete(self.apiclient)
15501612
self.cleanup = [o for o in self.cleanup if o != vpc]
15511613

0 commit comments

Comments
 (0)