Backport #9888 to 4.19: Fix Usage inconsistencies (#10712)

This commit is contained in:
Fabricio Duarte 2025-05-09 04:49:14 -03:00 committed by GitHub
parent f0838cdd30
commit 696bc50f3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 130 additions and 35 deletions

View File

@ -28,4 +28,6 @@ public interface UsageNetworksDao extends GenericDao<UsageNetworksVO, Long> {
void remove(long networkId, Date removed); void remove(long networkId, Date removed);
List<UsageNetworksVO> getUsageRecords(Long accountId, Date startDate, Date endDate); List<UsageNetworksVO> getUsageRecords(Long accountId, Date startDate, Date endDate);
List<UsageNetworksVO> listAll(long networkId);
} }

View File

@ -16,15 +16,16 @@
// under the License. // under the License.
package com.cloud.usage.dao; package com.cloud.usage.dao;
import com.cloud.network.Network;
import com.cloud.usage.UsageNetworksVO; import com.cloud.usage.UsageNetworksVO;
import com.cloud.utils.DateUtil; import com.cloud.utils.DateUtil;
import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.TransactionLegacy;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
@ -39,6 +40,14 @@ public class UsageNetworksDaoImpl extends GenericDaoBase<UsageNetworksVO, Long>
" account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " + " account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " +
" OR ((created <= ?) AND (removed >= ?)))"; " OR ((created <= ?) AND (removed >= ?)))";
private SearchBuilder<UsageNetworksVO> usageNetworksSearch;
@PostConstruct
public void init() {
usageNetworksSearch = createSearchBuilder();
usageNetworksSearch.and("networkId", usageNetworksSearch.entity().getNetworkId(), SearchCriteria.Op.EQ);
usageNetworksSearch.done();
}
@Override @Override
public void update(long networkId, long newNetworkOffering, String state) { public void update(long networkId, long newNetworkOffering, String state) {
@ -68,11 +77,10 @@ public class UsageNetworksDaoImpl extends GenericDaoBase<UsageNetworksVO, Long>
SearchCriteria<UsageNetworksVO> sc = this.createSearchCriteria(); SearchCriteria<UsageNetworksVO> sc = this.createSearchCriteria();
sc.addAnd("networkId", SearchCriteria.Op.EQ, networkId); sc.addAnd("networkId", SearchCriteria.Op.EQ, networkId);
sc.addAnd("removed", SearchCriteria.Op.NULL); sc.addAnd("removed", SearchCriteria.Op.NULL);
UsageNetworksVO vo = findOneBy(sc); List<UsageNetworksVO> usageNetworksVOs = listBy(sc);
if (vo != null) { for (UsageNetworksVO entry : usageNetworksVOs) {
vo.setRemoved(removed); entry.setRemoved(removed);
vo.setState(Network.State.Destroy.name()); update(entry.getId(), entry);
update(vo.getId(), vo);
} }
} catch (final Exception e) { } catch (final Exception e) {
txn.rollback(); txn.rollback();
@ -131,4 +139,11 @@ public class UsageNetworksDaoImpl extends GenericDaoBase<UsageNetworksVO, Long>
return usageRecords; return usageRecords;
} }
@Override
public List<UsageNetworksVO> listAll(long networkId) {
SearchCriteria<UsageNetworksVO> sc = usageNetworksSearch.create();
sc.setParameters("networkId", networkId);
return listBy(sc);
}
} }

View File

@ -24,6 +24,10 @@ import java.util.List;
public interface UsageVpcDao extends GenericDao<UsageVpcVO, Long> { public interface UsageVpcDao extends GenericDao<UsageVpcVO, Long> {
void update(UsageVpcVO usage); void update(UsageVpcVO usage);
void remove(long vpcId, Date removed); void remove(long vpcId, Date removed);
List<UsageVpcVO> getUsageRecords(Long accountId, Date startDate, Date endDate); List<UsageVpcVO> getUsageRecords(Long accountId, Date startDate, Date endDate);
List<UsageVpcVO> listAll(long vpcId);
} }

View File

@ -16,15 +16,16 @@
// under the License. // under the License.
package com.cloud.usage.dao; package com.cloud.usage.dao;
import com.cloud.network.vpc.Vpc;
import com.cloud.usage.UsageVpcVO; import com.cloud.usage.UsageVpcVO;
import com.cloud.utils.DateUtil; import com.cloud.utils.DateUtil;
import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.TransactionLegacy;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
@ -39,6 +40,15 @@ public class UsageVpcDaoImpl extends GenericDaoBase<UsageVpcVO, Long> implements
" account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " + " account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " +
" OR ((created <= ?) AND (removed >= ?)))"; " OR ((created <= ?) AND (removed >= ?)))";
private SearchBuilder<UsageVpcVO> usageVpcSearch;
@PostConstruct
public void init() {
usageVpcSearch = createSearchBuilder();
usageVpcSearch.and("vpcId", usageVpcSearch.entity().getVpcId(), SearchCriteria.Op.EQ);
usageVpcSearch.done();
}
@Override @Override
public void update(UsageVpcVO usage) { public void update(UsageVpcVO usage) {
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
@ -66,11 +76,10 @@ public class UsageVpcDaoImpl extends GenericDaoBase<UsageVpcVO, Long> implements
SearchCriteria<UsageVpcVO> sc = this.createSearchCriteria(); SearchCriteria<UsageVpcVO> sc = this.createSearchCriteria();
sc.addAnd("vpcId", SearchCriteria.Op.EQ, vpcId); sc.addAnd("vpcId", SearchCriteria.Op.EQ, vpcId);
sc.addAnd("removed", SearchCriteria.Op.NULL); sc.addAnd("removed", SearchCriteria.Op.NULL);
UsageVpcVO vo = findOneBy(sc); List<UsageVpcVO> usageVpcVOs = listBy(sc);
if (vo != null) { for (UsageVpcVO entry : usageVpcVOs) {
vo.setRemoved(removed); entry.setRemoved(removed);
vo.setState(Vpc.State.Inactive.name()); update(entry.getId(), entry);
update(vo.getId(), vo);
} }
} catch (final Exception e) { } catch (final Exception e) {
txn.rollback(); txn.rollback();
@ -128,4 +137,11 @@ public class UsageVpcDaoImpl extends GenericDaoBase<UsageVpcVO, Long> implements
return usageRecords; return usageRecords;
} }
@Override
public List<UsageVpcVO> listAll(long vpcId) {
SearchCriteria<UsageVpcVO> sc = usageVpcSearch.create();
sc.setParameters("vpcId", vpcId);
return listBy(sc);
}
} }

View File

@ -1540,7 +1540,7 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
//For volumes which are 'attached' successfully, set the 'deleted' column in the usage_storage table, //For volumes which are 'attached' successfully, set the 'deleted' column in the usage_storage table,
//so that the secondary storage should stop accounting and only primary will be accounted. //so that the secondary storage should stop accounting and only primary will be accounted.
SearchCriteria<UsageStorageVO> sc = _usageStorageDao.createSearchCriteria(); SearchCriteria<UsageStorageVO> sc = _usageStorageDao.createSearchCriteria();
sc.addAnd("id", SearchCriteria.Op.EQ, volId); sc.addAnd("entityId", SearchCriteria.Op.EQ, volId);
sc.addAnd("storageType", SearchCriteria.Op.EQ, StorageTypes.VOLUME); sc.addAnd("storageType", SearchCriteria.Op.EQ, StorageTypes.VOLUME);
List<UsageStorageVO> volumesVOs = _usageStorageDao.search(sc, null); List<UsageStorageVO> volumesVOs = _usageStorageDao.search(sc, null);
if (volumesVOs != null) { if (volumesVOs != null) {
@ -1595,7 +1595,8 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
//For Upload event add an entry to the usage_storage table. //For Upload event add an entry to the usage_storage table.
SearchCriteria<UsageStorageVO> sc = _usageStorageDao.createSearchCriteria(); SearchCriteria<UsageStorageVO> sc = _usageStorageDao.createSearchCriteria();
sc.addAnd("accountId", SearchCriteria.Op.EQ, event.getAccountId()); sc.addAnd("accountId", SearchCriteria.Op.EQ, event.getAccountId());
sc.addAnd("id", SearchCriteria.Op.EQ, volId); sc.addAnd("entityId", SearchCriteria.Op.EQ, volId);
sc.addAnd("storageType", SearchCriteria.Op.EQ, StorageTypes.VOLUME);
sc.addAnd("deleted", SearchCriteria.Op.NULL); sc.addAnd("deleted", SearchCriteria.Op.NULL);
List<UsageStorageVO> volumesVOs = _usageStorageDao.search(sc, null); List<UsageStorageVO> volumesVOs = _usageStorageDao.search(sc, null);
@ -1772,7 +1773,7 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
} else if (EventTypes.EVENT_LOAD_BALANCER_DELETE.equals(event.getType())) { } else if (EventTypes.EVENT_LOAD_BALANCER_DELETE.equals(event.getType())) {
SearchCriteria<UsageLoadBalancerPolicyVO> sc = _usageLoadBalancerPolicyDao.createSearchCriteria(); SearchCriteria<UsageLoadBalancerPolicyVO> sc = _usageLoadBalancerPolicyDao.createSearchCriteria();
sc.addAnd("accountId", SearchCriteria.Op.EQ, event.getAccountId()); sc.addAnd("accountId", SearchCriteria.Op.EQ, event.getAccountId());
sc.addAnd("id", SearchCriteria.Op.EQ, id); sc.addAnd("lbId", SearchCriteria.Op.EQ, id);
sc.addAnd("deleted", SearchCriteria.Op.NULL); sc.addAnd("deleted", SearchCriteria.Op.NULL);
List<UsageLoadBalancerPolicyVO> lbVOs = _usageLoadBalancerPolicyDao.search(sc, null); List<UsageLoadBalancerPolicyVO> lbVOs = _usageLoadBalancerPolicyDao.search(sc, null);
if (lbVOs.size() > 1) { if (lbVOs.size() > 1) {
@ -1806,7 +1807,7 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
} else if (EventTypes.EVENT_NET_RULE_DELETE.equals(event.getType())) { } else if (EventTypes.EVENT_NET_RULE_DELETE.equals(event.getType())) {
SearchCriteria<UsagePortForwardingRuleVO> sc = _usagePortForwardingRuleDao.createSearchCriteria(); SearchCriteria<UsagePortForwardingRuleVO> sc = _usagePortForwardingRuleDao.createSearchCriteria();
sc.addAnd("accountId", SearchCriteria.Op.EQ, event.getAccountId()); sc.addAnd("accountId", SearchCriteria.Op.EQ, event.getAccountId());
sc.addAnd("id", SearchCriteria.Op.EQ, id); sc.addAnd("pfId", SearchCriteria.Op.EQ, id);
sc.addAnd("deleted", SearchCriteria.Op.NULL); sc.addAnd("deleted", SearchCriteria.Op.NULL);
List<UsagePortForwardingRuleVO> pfVOs = _usagePortForwardingRuleDao.search(sc, null); List<UsagePortForwardingRuleVO> pfVOs = _usagePortForwardingRuleDao.search(sc, null);
if (pfVOs.size() > 1) { if (pfVOs.size() > 1) {
@ -2104,7 +2105,7 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
} else if (EventTypes.EVENT_VM_SNAPSHOT_OFF_PRIMARY.equals(event.getType())) { } else if (EventTypes.EVENT_VM_SNAPSHOT_OFF_PRIMARY.equals(event.getType())) {
QueryBuilder<UsageSnapshotOnPrimaryVO> sc = QueryBuilder.create(UsageSnapshotOnPrimaryVO.class); QueryBuilder<UsageSnapshotOnPrimaryVO> sc = QueryBuilder.create(UsageSnapshotOnPrimaryVO.class);
sc.and(sc.entity().getAccountId(), SearchCriteria.Op.EQ, event.getAccountId()); sc.and(sc.entity().getAccountId(), SearchCriteria.Op.EQ, event.getAccountId());
sc.and(sc.entity().getId(), SearchCriteria.Op.EQ, vmId); sc.and(sc.entity().getVmId(), SearchCriteria.Op.EQ, vmId);
sc.and(sc.entity().getName(), SearchCriteria.Op.EQ, name); sc.and(sc.entity().getName(), SearchCriteria.Op.EQ, name);
sc.and(sc.entity().getDeleted(), SearchCriteria.Op.NULL); sc.and(sc.entity().getDeleted(), SearchCriteria.Op.NULL);
List<UsageSnapshotOnPrimaryVO> vmsnaps = sc.list(); List<UsageSnapshotOnPrimaryVO> vmsnaps = sc.list();
@ -2142,31 +2143,88 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
} }
private void handleNetworkEvent(UsageEventVO event) { private void handleNetworkEvent(UsageEventVO event) {
Account account = _accountDao.findByIdIncludingRemoved(event.getAccountId()); String eventType = event.getType();
long domainId = account.getDomainId(); if (EventTypes.EVENT_NETWORK_DELETE.equals(eventType)) {
if (EventTypes.EVENT_NETWORK_DELETE.equals(event.getType())) { removeNetworkHelperEntry(event);
usageNetworksDao.remove(event.getResourceId(), event.getCreateDate()); } else if (EventTypes.EVENT_NETWORK_CREATE.equals(eventType)) {
} else if (EventTypes.EVENT_NETWORK_CREATE.equals(event.getType())) { createNetworkHelperEntry(event);
UsageNetworksVO usageNetworksVO = new UsageNetworksVO(event.getResourceId(), event.getOfferingId(), event.getZoneId(), event.getAccountId(), domainId, Network.State.Allocated.name(), event.getCreateDate(), null); } else if (EventTypes.EVENT_NETWORK_UPDATE.equals(eventType)) {
usageNetworksDao.persist(usageNetworksVO); updateNetworkHelperEntry(event);
} else if (EventTypes.EVENT_NETWORK_UPDATE.equals(event.getType())) {
usageNetworksDao.update(event.getResourceId(), event.getOfferingId(), event.getResourceType());
} else { } else {
s_logger.error(String.format("Unknown event type [%s] in Networks event parser. Skipping it.", event.getType())); s_logger.error(String.format("Unknown event type [%s] in Networks event parser. Skipping it.", eventType));
} }
} }
private void handleVpcEvent(UsageEventVO event) { private void removeNetworkHelperEntry(UsageEventVO event) {
long networkId = event.getResourceId();
s_logger.debug(String.format("Removing helper entries of network [%s].", networkId));
usageNetworksDao.remove(networkId, event.getCreateDate());
}
private void createNetworkHelperEntry(UsageEventVO event) {
long networkId = event.getResourceId();
Account account = _accountDao.findByIdIncludingRemoved(event.getAccountId()); Account account = _accountDao.findByIdIncludingRemoved(event.getAccountId());
long domainId = account.getDomainId(); long domainId = account.getDomainId();
if (EventTypes.EVENT_VPC_DELETE.equals(event.getType())) {
usageVpcDao.remove(event.getResourceId(), event.getCreateDate()); List<UsageNetworksVO> entries = usageNetworksDao.listAll(networkId);
} else if (EventTypes.EVENT_VPC_CREATE.equals(event.getType())) { if (!entries.isEmpty()) {
UsageVpcVO usageVPCVO = new UsageVpcVO(event.getResourceId(), event.getZoneId(), event.getAccountId(), domainId, Vpc.State.Enabled.name(), event.getCreateDate(), null); s_logger.warn(String.format("Received a NETWORK.CREATE event for a network [%s] that already has helper entries; " +
usageVpcDao.persist(usageVPCVO); "therefore, we will not create a new one.", networkId));
} else { return;
s_logger.error(String.format("Unknown event type [%s] in VPC event parser. Skipping it.", event.getType()));
} }
s_logger.debug(String.format("Creating a helper entry for network [%s].", networkId));
UsageNetworksVO usageNetworksVO = new UsageNetworksVO(networkId, event.getOfferingId(), event.getZoneId(),
event.getAccountId(), domainId, Network.State.Allocated.name(), event.getCreateDate(), null);
usageNetworksDao.persist(usageNetworksVO);
}
private void updateNetworkHelperEntry(UsageEventVO event) {
long networkId = event.getResourceId();
Account account = _accountDao.findByIdIncludingRemoved(event.getAccountId());
long domainId = account.getDomainId();
s_logger.debug(String.format("Marking previous helper entries of network [%s] as removed.", networkId));
usageNetworksDao.remove(networkId, event.getCreateDate());
s_logger.debug(String.format("Creating an updated helper entry for network [%s].", networkId));
UsageNetworksVO usageNetworksVO = new UsageNetworksVO(networkId, event.getOfferingId(), event.getZoneId(),
event.getAccountId(), domainId, event.getResourceType(), event.getCreateDate(), null);
usageNetworksDao.persist(usageNetworksVO);
}
private void handleVpcEvent(UsageEventVO event) {
String eventType = event.getType();
if (EventTypes.EVENT_VPC_DELETE.equals(eventType)) {
removeVpcHelperEntry(event);
} else if (EventTypes.EVENT_VPC_CREATE.equals(eventType)) {
createVpcHelperEntry(event);
} else {
s_logger.error(String.format("Unknown event type [%s] in VPC event parser. Skipping it.", eventType));
}
}
private void removeVpcHelperEntry(UsageEventVO event) {
long vpcId = event.getResourceId();
s_logger.debug(String.format("Removing helper entries of VPC [%s].", vpcId));
usageVpcDao.remove(vpcId, event.getCreateDate());
}
private void createVpcHelperEntry(UsageEventVO event) {
long vpcId = event.getResourceId();
Account account = _accountDao.findByIdIncludingRemoved(event.getAccountId());
long domainId = account.getDomainId();
List<UsageVpcVO> entries = usageVpcDao.listAll(vpcId);
if (!entries.isEmpty()) {
s_logger.warn(String.format("Active helper entries already exist for VPC [%s]; therefore, we will not create a new one.",
vpcId));
return;
}
s_logger.debug(String.format("Creating a helper entry for VPC [%s].", vpcId));
UsageVpcVO usageVPCVO = new UsageVpcVO(vpcId, event.getZoneId(), event.getAccountId(), domainId, Vpc.State.Enabled.name(), event.getCreateDate(), null);
usageVpcDao.persist(usageVPCVO);
} }
private class Heartbeat extends ManagedContextRunnable { private class Heartbeat extends ManagedContextRunnable {