|
19 | 19 |
|
20 | 20 | import static org.junit.Assert.assertFalse; |
21 | 21 | import static org.junit.Assert.assertTrue; |
| 22 | +import static org.mockito.ArgumentMatchers.any; |
| 23 | +import static org.mockito.ArgumentMatchers.anyBoolean; |
22 | 24 | import static org.mockito.ArgumentMatchers.anyLong; |
23 | 25 | import static org.mockito.Mockito.doReturn; |
24 | 26 | import static org.mockito.Mockito.lenient; |
25 | 27 | import static org.mockito.Mockito.mock; |
| 28 | +import static org.mockito.Mockito.never; |
26 | 29 | import static org.mockito.Mockito.verify; |
27 | 30 | import static org.mockito.Mockito.when; |
28 | 31 |
|
|
54 | 57 | import com.cloud.network.dao.IPAddressVO; |
55 | 58 | import com.cloud.network.dao.NetworkDao; |
56 | 59 | import com.cloud.network.dao.NetworkVO; |
| 60 | +import com.cloud.network.rules.FirewallRule; |
57 | 61 | import com.cloud.network.rules.StaticNat; |
58 | 62 | import com.cloud.network.rules.StaticNatImpl; |
| 63 | +import com.cloud.network.vpc.VpcManager; |
| 64 | +import com.cloud.dc.dao.VlanDao; |
| 65 | +import com.cloud.dc.VlanVO; |
59 | 66 | import com.cloud.offerings.NetworkOfferingVO; |
60 | 67 | import com.cloud.offerings.dao.NetworkOfferingDao; |
61 | 68 | import com.cloud.user.AccountVO; |
@@ -105,6 +112,12 @@ public class IpAddressManagerTest { |
105 | 112 | @Mock |
106 | 113 | AccountManager accountManagerMock; |
107 | 114 |
|
| 115 | + @Mock |
| 116 | + VpcManager vpcMgr; |
| 117 | + |
| 118 | + @Mock |
| 119 | + VlanDao vlanDao; |
| 120 | + |
108 | 121 | final long dummyID = 1L; |
109 | 122 |
|
110 | 123 | final String UUID = "uuid"; |
@@ -491,4 +504,166 @@ public void checkIfIpResourceCountShouldBeUpdatedTestIpIsAssociatedToVpcAndNotDe |
491 | 504 |
|
492 | 505 | Assert.assertTrue(result); |
493 | 506 | } |
| 507 | + |
| 508 | + |
| 509 | + private FirewallRule makeRule(Long networkId, Long vpcId, FirewallRule.Purpose purpose, |
| 510 | + FirewallRule.TrafficType trafficType) { |
| 511 | + FirewallRule rule = mock(FirewallRule.class); |
| 512 | + lenient().when(rule.getNetworkId()).thenReturn(networkId); |
| 513 | + lenient().when(rule.getVpcId()).thenReturn(vpcId); |
| 514 | + lenient().when(rule.getPurpose()).thenReturn(purpose); |
| 515 | + lenient().when(rule.getTrafficType()).thenReturn(trafficType); |
| 516 | + return rule; |
| 517 | + } |
| 518 | + |
| 519 | + /** Stub the two IP-association helper methods so they are no-ops. */ |
| 520 | + private void stubIpAssocHelpers() throws ResourceUnavailableException { |
| 521 | + doReturn(false).when(ipAddressManager).checkIfIpAssocRequired(any(Network.class), anyBoolean(), any()); |
| 522 | + } |
| 523 | + |
| 524 | + /** |
| 525 | + * Test: Non-VPC rules still resolve via networkId (backward compatibility). |
| 526 | + */ |
| 527 | + @Test |
| 528 | + public void applyRulesNonVpcRuleStillWorksViaNetworkId() throws ResourceUnavailableException { |
| 529 | + long networkId = 10L; |
| 530 | + NetworkVO network = mock(NetworkVO.class); |
| 531 | + when(network.getId()).thenReturn(networkId); |
| 532 | + when(networkDao.findById(networkId)).thenReturn(network); |
| 533 | + |
| 534 | + FirewallRule rule = makeRule(networkId, null, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Ingress); |
| 535 | + NetworkRuleApplier applier = mock(NetworkRuleApplier.class); |
| 536 | + |
| 537 | + when(ipAddressDao.listByAssociatedNetwork(networkId, null)).thenReturn(new ArrayList<>()); |
| 538 | + stubIpAssocHelpers(); |
| 539 | + |
| 540 | + boolean result = ipAddressManager.applyRules( |
| 541 | + Collections.singletonList(rule), FirewallRule.Purpose.Firewall, applier, false); |
| 542 | + |
| 543 | + assertTrue(result); |
| 544 | + verify(networkDao).findById(networkId); |
| 545 | + verify(applier).applyRules(network, FirewallRule.Purpose.Firewall, Collections.singletonList(rule)); |
| 546 | + } |
| 547 | + |
| 548 | + /** |
| 549 | + * Test: VPC rule resolves network via VpcManager.getVpcNetworks() |
| 550 | + * when networkId is null but vpcId is set. |
| 551 | + */ |
| 552 | + @Test |
| 553 | + public void applyRulesVpcRuleResolvesNetworkViaVpcManager() throws ResourceUnavailableException { |
| 554 | + long vpcId = 20L; |
| 555 | + long resolvedNetworkId = 30L; |
| 556 | + NetworkVO resolvedNetwork = mock(NetworkVO.class); |
| 557 | + when(resolvedNetwork.getId()).thenReturn(resolvedNetworkId); |
| 558 | + when(networkDao.findById(resolvedNetworkId)).thenReturn(resolvedNetwork); |
| 559 | + |
| 560 | + doReturn(Collections.singletonList(resolvedNetwork)).when(vpcMgr).getVpcNetworks(vpcId); |
| 561 | + |
| 562 | + FirewallRule rule = makeRule(null, vpcId, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Ingress); |
| 563 | + NetworkRuleApplier applier = mock(NetworkRuleApplier.class); |
| 564 | + |
| 565 | + IPAddressVO vpcIp = mock(IPAddressVO.class); |
| 566 | + when(vpcIp.getVlanId()).thenReturn(1L); |
| 567 | + VlanVO vlan = mock(VlanVO.class); |
| 568 | + when(ipAddressDao.listByAssociatedVpc(vpcId, null)).thenReturn(Collections.singletonList(vpcIp)); |
| 569 | + when(vlanDao.findById(1L)).thenReturn(vlan); |
| 570 | + |
| 571 | + stubIpAssocHelpers(); |
| 572 | + |
| 573 | + boolean result = ipAddressManager.applyRules( |
| 574 | + Collections.singletonList(rule), FirewallRule.Purpose.Firewall, applier, false); |
| 575 | + |
| 576 | + assertTrue(result); |
| 577 | + verify(vpcMgr).getVpcNetworks(vpcId); |
| 578 | + verify(ipAddressDao).listByAssociatedVpc(vpcId, null); |
| 579 | + verify(applier).applyRules(resolvedNetwork, FirewallRule.Purpose.Firewall, Collections.singletonList(rule)); |
| 580 | + } |
| 581 | + |
| 582 | + |
| 583 | + /** |
| 584 | + * Test: For VPC egress firewall rules, IP collection should be skipped. |
| 585 | + */ |
| 586 | + @Test |
| 587 | + public void applyRulesVpcEgressFirewallRuleSkipsIpCollection() throws ResourceUnavailableException { |
| 588 | + long vpcId = 20L; |
| 589 | + long resolvedNetworkId = 30L; |
| 590 | + NetworkVO resolvedNetwork = mock(NetworkVO.class); |
| 591 | + when(resolvedNetwork.getId()).thenReturn(resolvedNetworkId); |
| 592 | + when(networkDao.findById(resolvedNetworkId)).thenReturn(resolvedNetwork); |
| 593 | + doReturn(Collections.singletonList(resolvedNetwork)).when(vpcMgr).getVpcNetworks(vpcId); |
| 594 | + |
| 595 | + FirewallRule rule = makeRule(null, vpcId, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Egress); |
| 596 | + NetworkRuleApplier applier = mock(NetworkRuleApplier.class); |
| 597 | + |
| 598 | + stubIpAssocHelpers(); |
| 599 | + |
| 600 | + boolean result = ipAddressManager.applyRules( |
| 601 | + Collections.singletonList(rule), FirewallRule.Purpose.Firewall, applier, false); |
| 602 | + |
| 603 | + assertTrue(result); |
| 604 | + verify(ipAddressDao, never()).listByAssociatedVpc(anyLong(), any()); |
| 605 | + verify(applier).applyRules(resolvedNetwork, FirewallRule.Purpose.Firewall, Collections.singletonList(rule)); |
| 606 | + } |
| 607 | + |
| 608 | + /** |
| 609 | + * Test: VPC ingress firewall rules collect public IPs from VPC (listByAssociatedVpc), |
| 610 | + * NOT from network (listByAssociatedNetwork). |
| 611 | + */ |
| 612 | + @Test |
| 613 | + public void applyRulesVpcIngressRuleCollectsIpsFromVpcNotNetwork() throws ResourceUnavailableException { |
| 614 | + long vpcId = 20L; |
| 615 | + long resolvedNetworkId = 30L; |
| 616 | + NetworkVO resolvedNetwork = mock(NetworkVO.class); |
| 617 | + when(resolvedNetwork.getId()).thenReturn(resolvedNetworkId); |
| 618 | + when(networkDao.findById(resolvedNetworkId)).thenReturn(resolvedNetwork); |
| 619 | + doReturn(Collections.singletonList(resolvedNetwork)).when(vpcMgr).getVpcNetworks(vpcId); |
| 620 | + |
| 621 | + IPAddressVO vpcIp = mock(IPAddressVO.class); |
| 622 | + when(vpcIp.getVlanId()).thenReturn(1L); |
| 623 | + VlanVO vlan = mock(VlanVO.class); |
| 624 | + when(ipAddressDao.listByAssociatedVpc(vpcId, null)).thenReturn(Collections.singletonList(vpcIp)); |
| 625 | + when(vlanDao.findById(1L)).thenReturn(vlan); |
| 626 | + |
| 627 | + stubIpAssocHelpers(); |
| 628 | + |
| 629 | + NetworkRuleApplier applier = mock(NetworkRuleApplier.class); |
| 630 | + FirewallRule rule = makeRule(null, vpcId, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Ingress); |
| 631 | + |
| 632 | + ipAddressManager.applyRules(Collections.singletonList(rule), FirewallRule.Purpose.Firewall, applier, false); |
| 633 | + |
| 634 | + verify(ipAddressDao).listByAssociatedVpc(vpcId, null); |
| 635 | + verify(ipAddressDao, never()).listByAssociatedNetwork(anyLong(), any()); |
| 636 | + } |
| 637 | + |
| 638 | + /** |
| 639 | + * Test: Error handling respects continueOnError flag. |
| 640 | + * When continueOnError=true, exceptions are caught and false is returned. |
| 641 | + */ |
| 642 | + @Test |
| 643 | + public void applyRulesVpcRuleErrorHandlingWithContinueOnErrorTrue() throws ResourceUnavailableException { |
| 644 | + long vpcId = 20L; |
| 645 | + long resolvedNetworkId = 30L; |
| 646 | + NetworkVO resolvedNetwork = mock(NetworkVO.class); |
| 647 | + when(resolvedNetwork.getId()).thenReturn(resolvedNetworkId); |
| 648 | + when(networkDao.findById(resolvedNetworkId)).thenReturn(resolvedNetwork); |
| 649 | + doReturn(Collections.singletonList(resolvedNetwork)).when(vpcMgr).getVpcNetworks(vpcId); |
| 650 | + |
| 651 | + IPAddressVO vpcIp = mock(IPAddressVO.class); |
| 652 | + when(vpcIp.getVlanId()).thenReturn(1L); |
| 653 | + VlanVO vlan = mock(VlanVO.class); |
| 654 | + when(ipAddressDao.listByAssociatedVpc(vpcId, null)).thenReturn(Collections.singletonList(vpcIp)); |
| 655 | + when(vlanDao.findById(1L)).thenReturn(vlan); |
| 656 | + |
| 657 | + stubIpAssocHelpers(); |
| 658 | + |
| 659 | + NetworkRuleApplier applier = mock(NetworkRuleApplier.class); |
| 660 | + when(applier.applyRules(any(), any(), any())).thenThrow(new ResourceUnavailableException("test", Network.class, 0L)); |
| 661 | + |
| 662 | + FirewallRule rule = makeRule(null, vpcId, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Ingress); |
| 663 | + |
| 664 | + boolean result = ipAddressManager.applyRules( |
| 665 | + Collections.singletonList(rule), FirewallRule.Purpose.Firewall, applier, true); |
| 666 | + |
| 667 | + assertFalse(result); |
| 668 | + } |
494 | 669 | } |
0 commit comments