Soft delete port forwarding, load balancing and firewall rules (#13015)

This commit is contained in:
Bernardo De Marco Gonçalves 2026-06-29 18:21:19 -03:00 committed by GitHub
parent fb5e24fa08
commit 236e01aad8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 129 additions and 26 deletions

View File

@ -16,6 +16,7 @@
// under the License.
package com.cloud.network;
import java.util.Date;
import java.util.UUID;
import javax.persistence.Column;
@ -25,8 +26,11 @@ import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import com.cloud.network.rules.HealthCheckPolicy;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
@Entity
@ -68,6 +72,10 @@ public class LBHealthCheckPolicyVO implements HealthCheckPolicy {
@Column(name = "display", updatable = true, nullable = false)
protected boolean display = true;
@Column(name = GenericDao.REMOVED_COLUMN)
@Temporal(value = TemporalType.TIMESTAMP)
private Date removed;
protected LBHealthCheckPolicyVO() {
this.uuid = UUID.randomUUID().toString();
}

View File

@ -94,6 +94,7 @@ public class FirewallRulesDaoImpl extends GenericDaoBase<FirewallRuleVO, Long> i
ReleaseSearch.and("ipId", ReleaseSearch.entity().getSourceIpAddressId(), Op.EQ);
ReleaseSearch.and("purpose", ReleaseSearch.entity().getPurpose(), Op.EQ);
ReleaseSearch.and("ports", ReleaseSearch.entity().getSourcePortStart(), Op.IN);
ReleaseSearch.and("removed", ReleaseSearch.entity().getRemoved(), Op.NULL);
ReleaseSearch.done();
SystemRuleSearch = createSearchBuilder();

View File

@ -32,8 +32,9 @@ public class LBHealthCheckPolicyDaoImpl extends GenericDaoBase<LBHealthCheckPoli
public void remove(long loadBalancerId) {
SearchCriteria<LBHealthCheckPolicyVO> sc = createSearchCriteria();
sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId);
sc.addAnd("removed", SearchCriteria.Op.NULL);
expunge(sc);
remove(sc);
}
@Override
@ -41,8 +42,9 @@ public class LBHealthCheckPolicyDaoImpl extends GenericDaoBase<LBHealthCheckPoli
SearchCriteria<LBHealthCheckPolicyVO> sc = createSearchCriteria();
sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId);
sc.addAnd("revoke", SearchCriteria.Op.EQ, revoke);
sc.addAnd("removed", SearchCriteria.Op.NULL);
expunge(sc);
remove(sc);
}
@Override

View File

@ -28,4 +28,6 @@ public interface LBStickinessPolicyDao extends GenericDao<LBStickinessPolicyVO,
List<LBStickinessPolicyVO> listByLoadBalancerIdAndDisplayFlag(long loadBalancerId, boolean forDisplay);
List<LBStickinessPolicyVO> listByLoadBalancerId(long loadBalancerId, boolean revoke);
List<LBStickinessPolicyVO> listByLoadBalancerId(long loadBalancerId);
}

View File

@ -31,8 +31,9 @@ public class LBStickinessPolicyDaoImpl extends GenericDaoBase<LBStickinessPolicy
public void remove(long loadBalancerId) {
SearchCriteria<LBStickinessPolicyVO> sc = createSearchCriteria();
sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId);
sc.addAnd("removed", SearchCriteria.Op.NULL);
expunge(sc);
remove(sc);
}
@Override
@ -40,8 +41,9 @@ public class LBStickinessPolicyDaoImpl extends GenericDaoBase<LBStickinessPolicy
SearchCriteria<LBStickinessPolicyVO> sc = createSearchCriteria();
sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId);
sc.addAnd("revoke", SearchCriteria.Op.EQ, revoke);
sc.addAnd("removed", SearchCriteria.Op.NULL);
expunge(sc);
remove(sc);
}
@Override
@ -62,4 +64,10 @@ public class LBStickinessPolicyDaoImpl extends GenericDaoBase<LBStickinessPolicy
return listBy(sc);
}
@Override
public List<LBStickinessPolicyVO> listByLoadBalancerId(long loadBalancerId) {
SearchCriteria<LBStickinessPolicyVO> sc = createSearchCriteria();
sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId);
return listBy(sc);
}
}

View File

@ -17,6 +17,7 @@
package com.cloud.network.dao;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -30,9 +31,12 @@ import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import com.cloud.network.rules.StickinessPolicy;
import com.cloud.utils.Pair;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
@Entity
@ -68,6 +72,10 @@ public class LBStickinessPolicyVO implements StickinessPolicy {
@Column(name = "display", updatable = true, nullable = false)
protected boolean display = true;
@Column(name = GenericDao.REMOVED_COLUMN)
@Temporal(value = TemporalType.TIMESTAMP)
private Date removed;
protected LBStickinessPolicyVO() {
this.uuid = UUID.randomUUID().toString();
}

View File

@ -16,13 +16,17 @@
// under the License.
package com.cloud.network.dao;
import java.util.Date;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.api.InternalIdentity;
@Entity
@ -45,6 +49,10 @@ public class LoadBalancerCertMapVO implements InternalIdentity {
@Column(name = "revoke")
private boolean revoke = false;
@Column(name = GenericDao.REMOVED_COLUMN)
@Temporal(value = TemporalType.TIMESTAMP)
private Date removed;
public LoadBalancerCertMapVO() {
this.uuid = UUID.randomUUID().toString();
}

View File

@ -34,8 +34,9 @@ public class LoadBalancerVMMapDaoImpl extends GenericDaoBase<LoadBalancerVMMapVO
public void remove(long loadBalancerId) {
SearchCriteria<LoadBalancerVMMapVO> sc = createSearchCriteria();
sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId);
sc.addAnd("removed", SearchCriteria.Op.NULL);
expunge(sc);
remove(sc);
}
@Override
@ -43,11 +44,12 @@ public class LoadBalancerVMMapDaoImpl extends GenericDaoBase<LoadBalancerVMMapVO
SearchCriteria<LoadBalancerVMMapVO> sc = createSearchCriteria();
sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId);
sc.addAnd("instanceId", SearchCriteria.Op.IN, instanceIds.toArray());
sc.addAnd("removed", SearchCriteria.Op.NULL);
if (revoke != null) {
sc.addAnd("revoke", SearchCriteria.Op.EQ, revoke);
}
expunge(sc);
remove(sc);
}
@Override
@ -56,12 +58,13 @@ public class LoadBalancerVMMapDaoImpl extends GenericDaoBase<LoadBalancerVMMapVO
sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId);
sc.addAnd("instanceId", SearchCriteria.Op.IN, instanceId);
sc.addAnd("instanceIp", SearchCriteria.Op.EQ, instanceIp);
sc.addAnd("removed", SearchCriteria.Op.NULL);
if (revoke != null) {
sc.addAnd("revoke", SearchCriteria.Op.EQ, revoke);
}
expunge(sc);
remove(sc);
}

View File

@ -22,9 +22,14 @@ import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.api.InternalIdentity;
import java.util.Date;
@Entity
@Table(name = "load_balancer_vm_map")
public class LoadBalancerVMMapVO implements InternalIdentity {
@ -48,6 +53,10 @@ public class LoadBalancerVMMapVO implements InternalIdentity {
@Column(name = "state")
private String state;
@Column(name = GenericDao.REMOVED_COLUMN)
@Temporal(value = TemporalType.TIMESTAMP)
private Date removed = null;
public LoadBalancerVMMapVO() {
}

View File

@ -32,6 +32,8 @@ import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import com.cloud.utils.db.GenericDao;
@ -82,6 +84,10 @@ public class FirewallRuleVO implements FirewallRule {
@Column(name = GenericDao.CREATED_COLUMN)
Date created;
@Column(name = GenericDao.REMOVED_COLUMN)
@Temporal(value = TemporalType.TIMESTAMP)
private Date removed;
@Column(name = "network_id")
Long networkId;
@ -203,6 +209,10 @@ public class FirewallRuleVO implements FirewallRule {
return created;
}
public Date getRemoved() {
return removed;
}
protected FirewallRuleVO() {
uuid = UUID.randomUUID().toString();
}

View File

@ -17,6 +17,7 @@
package org.apache.cloudstack.region.gslb;
import java.util.Date;
import java.util.UUID;
import javax.persistence.Column;
@ -27,8 +28,11 @@ import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import com.cloud.region.ha.GlobalLoadBalancerRule;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
@Entity
@ -74,6 +78,10 @@ public class GlobalLoadBalancerRuleVO implements GlobalLoadBalancerRule {
@Column(name = "state")
GlobalLoadBalancerRule.State state;
@Column(name = GenericDao.REMOVED_COLUMN)
@Temporal(value = TemporalType.TIMESTAMP)
private Date removed;
public GlobalLoadBalancerRuleVO() {
uuid = UUID.randomUUID().toString();
}

View File

@ -288,3 +288,22 @@ WHERE rule = 'quotaStatement' AND NOT EXISTS(SELECT 1 FROM cloud.role_permission
-- Add description for secondary IP addresses
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nic_secondary_ips', 'description', 'VARCHAR(2048) DEFAULT NULL');
-- Soft delete port forwarding, load balancing and firewall rules
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.firewall_rules', 'removed', 'datetime DEFAULT NULL');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.load_balancer_vm_map', 'removed', 'datetime DEFAULT NULL');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.load_balancer_cert_map', 'removed', 'datetime DEFAULT NULL');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.load_balancer_healthcheck_policies', 'removed', 'datetime DEFAULT NULL');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.load_balancer_stickiness_policies', 'removed', 'datetime DEFAULT NULL');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.global_load_balancer_lb_rule_map', 'removed', 'datetime DEFAULT NULL');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.elastic_lb_vm_map', 'removed', 'datetime DEFAULT NULL');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.tungsten_lb_health_monitor', 'removed', 'datetime DEFAULT NULL');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.global_load_balancing_rules', 'removed', 'datetime DEFAULT NULL');
ALTER TABLE `cloud`.`load_balancer_vm_map`
DROP KEY `load_balancer_id`,
ADD UNIQUE KEY `load_balancer_id` (`load_balancer_id`, `instance_id`, `instance_ip`, `removed`);
ALTER TABLE `cloud`.`global_load_balancer_lb_rule_map`
DROP KEY `gslb_rule_id`,
ADD UNIQUE KEY `gslb_rule_id` (`gslb_rule_id`, `lb_rule_id`, `removed`);

View File

@ -27,11 +27,16 @@ import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.SecondaryTable;
import javax.persistence.SecondaryTables;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.api.InternalIdentity;
import com.cloud.utils.net.Ip;
import java.util.Date;
@Entity
@Table(name = "elastic_lb_vm_map")
@SecondaryTables({@SecondaryTable(name = "user_ip_address", pkJoinColumns = {@PrimaryKeyJoinColumn(name = "ip_addr_id", referencedColumnName = "id")})})
@ -57,6 +62,10 @@ public class ElasticLbVmMapVO implements InternalIdentity {
@Enumerated(value = EnumType.STRING)
private Ip address = null;
@Column(name = GenericDao.REMOVED_COLUMN)
@Temporal(value = TemporalType.TIMESTAMP)
private Date removed;
public ElasticLbVmMapVO() {
}

View File

@ -16,9 +16,11 @@
// under the License.
package org.apache.cloudstack.network.tungsten.dao;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.api.Identity;
import org.apache.cloudstack.api.InternalIdentity;
import java.util.Date;
import java.util.UUID;
import javax.persistence.Column;
@ -28,6 +30,8 @@ import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
@Table(name = "tungsten_lb_health_monitor")
@ -66,6 +70,10 @@ public class TungstenFabricLBHealthMonitorVO implements InternalIdentity, Identi
@Column(name = "url_path")
private String urlPath;
@Column(name = GenericDao.REMOVED_COLUMN)
@Temporal(value = TemporalType.TIMESTAMP)
private Date removed = null;
public TungstenFabricLBHealthMonitorVO() {
this.uuid = UUID.randomUUID().toString();
}

View File

@ -862,7 +862,7 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
success = false;
}
} else {
_lb2stickinesspoliciesDao.expunge(stickinessPolicyId);
_lb2stickinesspoliciesDao.remove(stickinessPolicyId);
}
return success;
}
@ -1664,6 +1664,12 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
_lb2healthcheckDao.persist(lbHealthCheck);
}
List<LBStickinessPolicyVO> stickinessPolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(loadBalancerId);
for (LBStickinessPolicyVO stickinessPolicy : stickinessPolicies) {
stickinessPolicy.setRevoke(true);
_lb2stickinesspoliciesDao.persist(stickinessPolicy);
}
if (generateUsageEvent) {
// Generate usage event right after all rules were marked for revoke
Network network = _networkModel.getNetwork(lb.getNetworkId());
@ -2678,7 +2684,12 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
@Override
public void removeLBRule(LoadBalancer rule) {
// remove the rule
FirewallRule relatedFirewallRule = _firewallDao.findByRelatedId(rule.getId());
if (relatedFirewallRule != null) {
logger.debug("Load balancer [{}] has a related firewall rule [{}]. Removing it.", rule.getUuid(), relatedFirewallRule.getUuid());
_firewallDao.remove(relatedFirewallRule.getId());
}
_lbDao.remove(rule.getId());
}

View File

@ -939,10 +939,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase):
lb_rule = self.create_LB_Rule(public_ip_1, network_1, [vm_2, vm_1])
public_ip_1.delete(self.apiclient)
self.cleanup.remove(public_ip_1)
with self.assertRaises(Exception):
lb_rules = LoadBalancerRule.list(self.apiclient,
id=lb_rule.id,
listall=True
)
lb_rule_after_deletion = LoadBalancerRule.list(self.apiclient, id=lb_rule.id, listall=True)
self.assertEqual(lb_rule_after_deletion, None, 'Load balancer rule should be deleted.')
return

View File

@ -470,10 +470,8 @@ class TestPortForwarding(cloudstackTestCase):
except Exception as e:
self.fail("NAT Rule Deletion Failed: %s" % e)
# NAT rule listing should fail as the nat rule does not exist
with self.assertRaises(Exception):
list_nat_rules(self.apiclient,
id=nat_rule.id)
nat_rules_after_deletion = list_nat_rules(self.apiclient, id=nat_rule.id)
self.assertEqual(nat_rules_after_deletion, None, "Check that the port forwarding rule has been deleted.")
# Check if the Public SSH port is inaccessible
with self.assertRaises(Exception):
@ -585,13 +583,8 @@ class TestPortForwarding(cloudstackTestCase):
nat_rule.delete(self.apiclient)
try:
list_nat_rule_response = list_nat_rules(
self.apiclient,
id=nat_rule.id
)
except CloudstackAPIException:
logger.debug("Nat Rule is deleted")
nat_rules_after_deletion = list_nat_rules(self.apiclient, id=nat_rule.id)
self.assertEqual(nat_rules_after_deletion, None, "Check that the port forwarding rule has been deleted.")
# Check if the Public SSH port is inaccessible
with self.assertRaises(Exception):