diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index d4d40dbb1e8..2cb650005e4 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -184,6 +184,8 @@ public class EventTypes { public static final String EVENT_SECURITY_GROUP_REVOKE_EGRESS = "SG.REVOKE.EGRESS"; public static final String EVENT_SECURITY_GROUP_CREATE = "SG.CREATE"; public static final String EVENT_SECURITY_GROUP_DELETE = "SG.DELETE"; + public static final String EVENT_SECURITY_GROUP_ASSIGN = "SG.ASSIGN"; + public static final String EVENT_SECURITY_GROUP_REMOVE = "SG.REMOVE"; // Host public static final String EVENT_HOST_RECONNECT = "HOST.RECONNECT"; diff --git a/core/src/com/cloud/event/UsageEventVO.java b/core/src/com/cloud/event/UsageEventVO.java index e7c33a38410..d717233e651 100644 --- a/core/src/com/cloud/event/UsageEventVO.java +++ b/core/src/com/cloud/event/UsageEventVO.java @@ -116,6 +116,16 @@ public class UsageEventVO implements UsageEvent { this.resourceType = resourceType; } + //Security Group usage event + public UsageEventVO(String usageType, long accountId, + long zoneId, long vmId, long securityGroupId) { + this.type = usageType; + this.accountId = accountId; + this.zoneId = zoneId; + this.resourceId = vmId; + this.offeringId = securityGroupId; + } + @Override public long getId() { return id; diff --git a/server/src/com/cloud/api/commands/GetUsageRecordsCmd.java b/server/src/com/cloud/api/commands/GetUsageRecordsCmd.java index 183e7aed4c3..968c7792d62 100644 --- a/server/src/com/cloud/api/commands/GetUsageRecordsCmd.java +++ b/server/src/com/cloud/api/commands/GetUsageRecordsCmd.java @@ -318,6 +318,10 @@ public class GetUsageRecordsCmd extends BaseListCmd { } else if(usageRecord.getUsageType() == UsageTypes.VPN_USERS){ //VPN User ID usageRecResponse.setUsageId(usageRecord.getUsageId().toString()); + + } else if(usageRecord.getUsageType() == UsageTypes.SECURITY_GROUP){ + //Security Group Id + usageRecResponse.setUsageId(identityDao.getIdentityUuid("security_group", usageRecord.getUsageId().toString())); } if (usageRecord.getRawUsage() != null) { diff --git a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java index d9b0dc9ba37..04f3774dc11 100755 --- a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java +++ b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java @@ -57,6 +57,8 @@ import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; +import com.cloud.event.UsageEventVO; +import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.OperationTimedoutException; @@ -152,7 +154,9 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG DomainManager _domainMgr; @Inject ProjectManager _projectMgr; - + @Inject + UsageEventDao _usageEventDao; + ScheduledExecutorService _executorPool; ScheduledExecutorService _cleanupExecutor; @@ -449,6 +453,10 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG List groupsForVm = _securityGroupVMMapDao.listByInstanceId(vm.getId()); // For each group, find the security rules that allow the group for (SecurityGroupVMMapVO mapVO : groupsForVm) {// FIXME: use custom sql in the dao + //Add usage events for security group assign + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_SECURITY_GROUP_ASSIGN, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), mapVO.getSecurityGroupId()); + _usageEventDao.persist(usageEvent); + List allowingRules = _securityGroupRuleDao.listByAllowedSecurityGroupId(mapVO.getSecurityGroupId()); // For each security rule that allows a group that the vm belongs to, find the group it belongs to affectedVms.addAll(getAffectedVmsForSecurityRules(allowingRules)); @@ -461,6 +469,10 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG List groupsForVm = _securityGroupVMMapDao.listByInstanceId(vm.getId()); // For each group, find the security rules rules that allow the group for (SecurityGroupVMMapVO mapVO : groupsForVm) {// FIXME: use custom sql in the dao + //Add usage events for security group remove + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_SECURITY_GROUP_REMOVE, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), mapVO.getSecurityGroupId()); + _usageEventDao.persist(usageEvent); + List allowingRules = _securityGroupRuleDao.listByAllowedSecurityGroupId(mapVO.getSecurityGroupId()); // For each security rule that allows a group that the vm belongs to, find the group it belongs to affectedVms.addAll(getAffectedVmsForSecurityRules(allowingRules)); diff --git a/server/src/com/cloud/usage/UsageSecurityGroupVO.java b/server/src/com/cloud/usage/UsageSecurityGroupVO.java new file mode 100644 index 00000000000..70fb0e6285c --- /dev/null +++ b/server/src/com/cloud/usage/UsageSecurityGroupVO.java @@ -0,0 +1,100 @@ +/** + * * Copyright (C) 2012 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +@Entity +@Table(name="usage_security_group") +public class UsageSecurityGroupVO { + + @Column(name="zone_id") + private long zoneId; + + @Column(name="account_id") + private long accountId; + + @Column(name="domain_id") + private long domainId; + + @Column(name="vm_instance_id") + private long vmInstanceId; + + @Column(name="security_group_id") + private Long securityGroupId; + + @Column(name="created") + @Temporal(value=TemporalType.TIMESTAMP) + private Date created = null; + + @Column(name="deleted") + @Temporal(value=TemporalType.TIMESTAMP) + private Date deleted = null; + + public UsageSecurityGroupVO(){ + } + + public UsageSecurityGroupVO(long zoneId, long accountId, long domainId, long vmInstanceId, long securityGroupId, Date created, Date deleted) { + this.zoneId = zoneId; + this.accountId = accountId; + this.domainId = domainId; + this.vmInstanceId = vmInstanceId; + this.securityGroupId = securityGroupId; + this.created = created; + this.deleted = deleted; + } + + public long getZoneId() { + return zoneId; + } + + public long getAccountId() { + return accountId; + } + + public long getDomainId() { + return domainId; + } + + public long getVmInstanceId() { + return vmInstanceId; + } + + public Long getSecurityGroupId() { + return securityGroupId; + } + + public Date getCreated() { + return created; + } + + public Date getDeleted() { + return deleted; + } + public void setDeleted(Date deleted) { + this.deleted = deleted; + } +} diff --git a/server/src/com/cloud/usage/dao/UsageSecurityGroupDao.java b/server/src/com/cloud/usage/dao/UsageSecurityGroupDao.java new file mode 100644 index 00000000000..8390294d0c3 --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageSecurityGroupDao.java @@ -0,0 +1,31 @@ +/** + * * Copyright (C) 2012 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.util.Date; +import java.util.List; + +import com.cloud.usage.UsageSecurityGroupVO; +import com.cloud.utils.db.GenericDao; + +public interface UsageSecurityGroupDao extends GenericDao { + public void update(UsageSecurityGroupVO usage); + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page); +} diff --git a/server/src/com/cloud/usage/dao/UsageSecurityGroupDaoImpl.java b/server/src/com/cloud/usage/dao/UsageSecurityGroupDaoImpl.java new file mode 100644 index 00000000000..e25ea69747d --- /dev/null +++ b/server/src/com/cloud/usage/dao/UsageSecurityGroupDaoImpl.java @@ -0,0 +1,152 @@ +/** + * * Copyright (C) 2012 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.dao; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.usage.UsageSecurityGroupVO; +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.Transaction; + +@Local(value={UsageSecurityGroupDao.class}) +public class UsageSecurityGroupDaoImpl extends GenericDaoBase implements UsageSecurityGroupDao { + public static final Logger s_logger = Logger.getLogger(UsageSecurityGroupDaoImpl.class.getName()); + + protected static final String UPDATE_DELETED = "UPDATE usage_security_group SET deleted = ? WHERE account_id = ? AND vm_instance_id = ? AND security_group_id = ? and deleted IS NULL"; + protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT zone_id, account_id, domain_id, vm_instance_id, security_group_id, created, deleted " + + "FROM usage_security_group " + + "WHERE account_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; + protected static final String GET_USAGE_RECORDS_BY_DOMAIN = "SELECT zone_id, account_id, domain_id, vm_instance_id, security_group_id, created, deleted " + + "FROM usage_security_group " + + "WHERE domain_id = ? AND ((deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?)))"; + protected static final String GET_ALL_USAGE_RECORDS = "SELECT zone_id, account_id, domain_id, vm_instance_id, security_group_id, created, deleted " + + "FROM usage_security_group " + + "WHERE (deleted IS NULL) OR (created BETWEEN ? AND ?) OR " + + " (deleted BETWEEN ? AND ?) OR ((created <= ?) AND (deleted >= ?))"; + + public UsageSecurityGroupDaoImpl() {} + + public void update(UsageSecurityGroupVO usage) { + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + try { + txn.start(); + if (usage.getDeleted() != null) { + pstmt = txn.prepareAutoCloseStatement(UPDATE_DELETED); + pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), usage.getDeleted())); + pstmt.setLong(2, usage.getAccountId()); + pstmt.setLong(3, usage.getVmInstanceId()); + pstmt.setLong(4, usage.getSecurityGroupId()); + } + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error updating UsageSecurityGroupVO", e); + } finally { + txn.close(); + } + } + + @Override + public List getUsageRecords(Long accountId, Long domainId, Date startDate, Date endDate, boolean limit, int page) { + List usageRecords = new ArrayList(); + + Long param1 = null; + String sql = null; + if (accountId != null) { + sql = GET_USAGE_RECORDS_BY_ACCOUNT; + param1 = accountId; + } else if (domainId != null) { + sql = GET_USAGE_RECORDS_BY_DOMAIN; + param1 = domainId; + } else { + sql = GET_ALL_USAGE_RECORDS; + } + + if (limit) { + int startIndex = 0; + if (page > 0) { + startIndex = 500 * (page-1); + } + sql += " LIMIT " + startIndex + ",500"; + } + + Transaction txn = Transaction.open(Transaction.USAGE_DB); + PreparedStatement pstmt = null; + + try { + int i = 1; + pstmt = txn.prepareAutoCloseStatement(sql); + if (param1 != null) { + pstmt.setLong(i++, param1); + } + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + //zoneId, account_id, domain_id, vm_instance_id, security_group_id, created, deleted + Long zoneId = Long.valueOf(rs.getLong(1)); + Long acctId = Long.valueOf(rs.getLong(2)); + Long dId = Long.valueOf(rs.getLong(3)); + long vmId = Long.valueOf(rs.getLong(4)); + long sgId = Long.valueOf(rs.getLong(5)); + Date createdDate = null; + Date deletedDate = null; + String createdTS = rs.getString(6); + String deletedTS = rs.getString(7); + + + if (createdTS != null) { + createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); + } + if (deletedTS != null) { + deletedDate = DateUtil.parseDateString(s_gmtTimeZone, deletedTS); + } + + usageRecords.add(new UsageSecurityGroupVO(zoneId, acctId, dId, vmId, sgId, createdDate, deletedDate)); + } + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Error getting usage records", e); + } finally { + txn.close(); + } + + return usageRecords; + } +} diff --git a/setup/db/create-schema-premium.sql b/setup/db/create-schema-premium.sql index 1c960a95088..2056005c4f8 100644 --- a/setup/db/create-schema-premium.sql +++ b/setup/db/create-schema-premium.sql @@ -28,6 +28,7 @@ DROP TABLE IF EXISTS `cloud_usage`.`usage_port_forwarding`; DROP TABLE IF EXISTS `cloud_usage`.`usage_network_offering`; DROP TABLE IF EXISTS `cloud_usage`.`usage_event`; DROP TABLE IF EXISTS `cloud_usage`.`usage_vpn_user`; +DROP TABLE IF EXISTS `cloud_usage`.`usage_security_group`; CREATE TABLE `cloud_usage`.`cloud_usage` ( `id` bigint unsigned NOT NULL auto_increment, @@ -263,6 +264,19 @@ ALTER TABLE `cloud_usage`.`usage_vpn_user` ADD INDEX `i_usage_vpn_user__account_ ALTER TABLE `cloud_usage`.`usage_vpn_user` ADD INDEX `i_usage_vpn_user__created`(`created`); ALTER TABLE `cloud_usage`.`usage_vpn_user` ADD INDEX `i_usage_vpn_user__deleted`(`deleted`); +CREATE TABLE `cloud_usage`.`usage_security_group` ( + `zone_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `domain_id` bigint unsigned NOT NULL, + `vm_instance_id` bigint unsigned NOT NULL, + `security_group_id` bigint unsigned NOT NULL, + `created` DATETIME NOT NULL, + `deleted` DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `cloud_usage`.`usage_security_group` ADD INDEX `i_usage_security_group__account_id`(`account_id`); +ALTER TABLE `cloud_usage`.`usage_security_group` ADD INDEX `i_usage_security_group__created`(`created`); +ALTER TABLE `cloud_usage`.`usage_security_group` ADD INDEX `i_usage_security_group__deleted`(`deleted`); CREATE TABLE `cloud`.`netapp_volume` ( `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id', diff --git a/setup/db/db/schema-2214to30.sql b/setup/db/db/schema-2214to30.sql index 91009305d14..a49bfd586ec 100755 --- a/setup/db/db/schema-2214to30.sql +++ b/setup/db/db/schema-2214to30.sql @@ -647,3 +647,17 @@ UPDATE `cloud`.`configuration` SET category = 'Hidden' WHERE name = 'kvm.private UPDATE `cloud`.`configuration` SET category = 'Hidden' WHERE name = 'kvm.guest.network.device'; ALTER TABLE `cloud`.`physical_network_traffic_types` ADD COLUMN `ovm_network_label` varchar(255) COMMENT 'The network name label of the physical device dedicated to this traffic on a Ovm host'; + +CREATE TABLE `cloud_usage`.`usage_security_group` ( + `zone_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + `domain_id` bigint unsigned NOT NULL, + `vm_instance_id` bigint unsigned NOT NULL, + `security_group_id` bigint unsigned NOT NULL, + `created` DATETIME NOT NULL, + `deleted` DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `cloud_usage`.`usage_security_group` ADD INDEX `i_usage_security_group__account_id`(`account_id`); +ALTER TABLE `cloud_usage`.`usage_security_group` ADD INDEX `i_usage_security_group__created`(`created`); +ALTER TABLE `cloud_usage`.`usage_security_group` ADD INDEX `i_usage_security_group__deleted`(`deleted`); diff --git a/usage/conf/usage-components.xml.in b/usage/conf/usage-components.xml.in index 84eb526834c..271490b6ef4 100644 --- a/usage/conf/usage-components.xml.in +++ b/usage/conf/usage-components.xml.in @@ -48,6 +48,7 @@ + diff --git a/usage/src/com/cloud/usage/UsageManagerImpl.java b/usage/src/com/cloud/usage/UsageManagerImpl.java index fcee7d3d8c2..b6f59b4a993 100644 --- a/usage/src/com/cloud/usage/UsageManagerImpl.java +++ b/usage/src/com/cloud/usage/UsageManagerImpl.java @@ -50,6 +50,7 @@ import com.cloud.usage.dao.UsageLoadBalancerPolicyDao; import com.cloud.usage.dao.UsageNetworkDao; import com.cloud.usage.dao.UsageNetworkOfferingDao; import com.cloud.usage.dao.UsagePortForwardingRuleDao; +import com.cloud.usage.dao.UsageSecurityGroupDao; import com.cloud.usage.dao.UsageStorageDao; import com.cloud.usage.dao.UsageVMInstanceDao; import com.cloud.usage.dao.UsageVPNUserDao; @@ -59,6 +60,7 @@ import com.cloud.usage.parser.LoadBalancerUsageParser; import com.cloud.usage.parser.NetworkOfferingUsageParser; import com.cloud.usage.parser.NetworkUsageParser; import com.cloud.usage.parser.PortForwardingUsageParser; +import com.cloud.usage.parser.SecurityGroupUsageParser; import com.cloud.usage.parser.StorageUsageParser; import com.cloud.usage.parser.VMInstanceUsageParser; import com.cloud.usage.parser.VPNUserUsageParser; @@ -104,6 +106,7 @@ public class UsageManagerImpl implements UsageManager, Runnable { private final UsagePortForwardingRuleDao m_usagePortForwardingRuleDao = _locator.getDao(UsagePortForwardingRuleDao.class); private final UsageNetworkOfferingDao m_usageNetworkOfferingDao = _locator.getDao(UsageNetworkOfferingDao.class); private final UsageVPNUserDao m_usageVPNUserDao = _locator.getDao(UsageVPNUserDao.class); + private final UsageSecurityGroupDao m_usageSecurityGroupDao = _locator.getDao(UsageSecurityGroupDao.class); private final UsageJobDao m_usageJobDao = _locator.getDao(UsageJobDao.class); @Inject protected AlertManager _alertMgr; @Inject protected UsageEventDao _usageEventDao; @@ -721,6 +724,13 @@ public class UsageManagerImpl implements UsageManager, Runnable { } } + parsed = SecurityGroupUsageParser.parse(account, currentStartDate, currentEndDate); + if (s_logger.isDebugEnabled()) { + if (!parsed) { + s_logger.debug("Security Group usage successfully parsed? " + parsed + " (for account: " + account.getAccountName() + ", id: " + account.getId() + ")"); + } + } + parsed = LoadBalancerUsageParser.parse(account, currentStartDate, currentEndDate); if (s_logger.isDebugEnabled()) { if (!parsed) { @@ -779,6 +789,8 @@ public class UsageManagerImpl implements UsageManager, Runnable { createNetworkOfferingEvent(event); } else if (isVPNUserEvent(eventType)) { createVPNUserEvent(event); + } else if (isSecurityGroupEvent(eventType)) { + createSecurityGroupEvent(event); } } @@ -840,6 +852,12 @@ public class UsageManagerImpl implements UsageManager, Runnable { if (eventType == null) return false; return eventType.startsWith("VPN.USER"); } + + private boolean isSecurityGroupEvent(String eventType) { + if (eventType == null) return false; + return (eventType.equals(EventTypes.EVENT_SECURITY_GROUP_ASSIGN) || + eventType.equals(EventTypes.EVENT_SECURITY_GROUP_REMOVE)); + } private void createVMHelperEvent(UsageEventVO event) { @@ -1354,6 +1372,41 @@ public class UsageManagerImpl implements UsageManager, Runnable { } } } + + private void createSecurityGroupEvent(UsageEventVO event) { + + long zoneId = -1L; + + long vmId = event.getResourceId(); + long sgId = event.getOfferingId(); + + if (EventTypes.EVENT_SECURITY_GROUP_ASSIGN.equals(event.getType())) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Assigning : security group"+ sgId +" to Vm: " + vmId + " for account: " + event.getAccountId()); + } + zoneId = event.getZoneId(); + Account acct = m_accountDao.findByIdIncludingRemoved(event.getAccountId()); + UsageSecurityGroupVO securityGroup = new UsageSecurityGroupVO(zoneId, event.getAccountId(), acct.getDomainId(), vmId, sgId,event.getCreateDate(), null); + m_usageSecurityGroupDao.persist(securityGroup); + } else if (EventTypes.EVENT_SECURITY_GROUP_REMOVE.equals(event.getType())) { + SearchCriteria sc = m_usageSecurityGroupDao.createSearchCriteria(); + sc.addAnd("accountId", SearchCriteria.Op.EQ, event.getAccountId()); + sc.addAnd("vmInstanceId", SearchCriteria.Op.EQ, vmId); + sc.addAnd("securityGroupId", SearchCriteria.Op.EQ, sgId); + sc.addAnd("deleted", SearchCriteria.Op.NULL); + List sgVOs = m_usageSecurityGroupDao.search(sc, null); + if (sgVOs.size() > 1) { + s_logger.warn("More that one usage entry for security group: "+ sgId +" for Vm: " + vmId+" assigned to account: " + event.getAccountId() + "; marking them all as deleted..."); + } + for (UsageSecurityGroupVO sgVO : sgVOs) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("deleting security group: " + sgVO.getSecurityGroupId() + " from Vm: " + sgVO.getVmInstanceId()); + } + sgVO.setDeleted(event.getCreateDate()); // there really shouldn't be more than one + m_usageSecurityGroupDao.update(sgVO); + } + } + } private class Heartbeat implements Runnable { public void run() { diff --git a/usage/src/com/cloud/usage/parser/SecurityGroupUsageParser.java b/usage/src/com/cloud/usage/parser/SecurityGroupUsageParser.java new file mode 100644 index 00000000000..1568aeb4b94 --- /dev/null +++ b/usage/src/com/cloud/usage/parser/SecurityGroupUsageParser.java @@ -0,0 +1,168 @@ + +/** + * * Copyright (C) 2012 Citrix Systems, Inc. All rights reserved +* + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.usage.parser; + +import java.text.DecimalFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.cloud.usage.UsageSecurityGroupVO; +import com.cloud.usage.UsageServer; +import com.cloud.usage.UsageTypes; +import com.cloud.usage.UsageVO; +import com.cloud.usage.dao.UsageDao; +import com.cloud.usage.dao.UsageSecurityGroupDao; +import com.cloud.user.AccountVO; +import com.cloud.utils.Pair; +import com.cloud.utils.component.ComponentLocator; + +public class SecurityGroupUsageParser { + public static final Logger s_logger = Logger.getLogger(SecurityGroupUsageParser.class.getName()); + + private static ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); + private static UsageDao m_usageDao = _locator.getDao(UsageDao.class); + private static UsageSecurityGroupDao m_usageSecurityGroupDao = _locator.getDao(UsageSecurityGroupDao.class); + + public static boolean parse(AccountVO account, Date startDate, Date endDate) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Parsing all SecurityGroup usage events for account: " + account.getId()); + } + if ((endDate == null) || endDate.after(new Date())) { + endDate = new Date(); + } + + // - query usage_volume table with the following criteria: + // - look for an entry for accountId with start date in the given range + // - look for an entry for accountId with end date in the given range + // - look for an entry for accountId with end date null (currently running vm or owned IP) + // - look for an entry for accountId with start date before given range *and* end date after given range + List usageSGs = m_usageSecurityGroupDao.getUsageRecords(account.getId(), account.getDomainId(), startDate, endDate, false, 0); + + if(usageSGs.isEmpty()){ + s_logger.debug("No SecurityGroup usage events for this period"); + return true; + } + + // This map has both the running time *and* the usage amount. + Map> usageMap = new HashMap>(); + Map sgMap = new HashMap(); + + // loop through all the security groups, create a usage record for each + for (UsageSecurityGroupVO usageSG : usageSGs) { + long vmId = usageSG.getVmInstanceId(); + long sgId = usageSG.getSecurityGroupId(); + String key = ""+vmId+"SG"+sgId; + + sgMap.put(key, new SGInfo(vmId, usageSG.getZoneId(), sgId)); + + Date sgCreateDate = usageSG.getCreated(); + Date sgDeleteDate = usageSG.getDeleted(); + + if ((sgDeleteDate == null) || sgDeleteDate.after(endDate)) { + sgDeleteDate = endDate; + } + + // clip the start date to the beginning of our aggregation range if the vm has been running for a while + if (sgCreateDate.before(startDate)) { + sgCreateDate = startDate; + } + + long currentDuration = (sgDeleteDate.getTime() - sgCreateDate.getTime()) + 1; // make sure this is an inclusive check for milliseconds (i.e. use n - m + 1 to find total number of millis to charge) + + + updateSGUsageData(usageMap, key, usageSG.getVmInstanceId(), currentDuration); + } + + for (String sgIdKey : usageMap.keySet()) { + Pair sgtimeInfo = usageMap.get(sgIdKey); + long useTime = sgtimeInfo.second().longValue(); + + // Only create a usage record if we have a runningTime of bigger than zero. + if (useTime > 0L) { + SGInfo info = sgMap.get(sgIdKey); + createUsageRecord(UsageTypes.SECURITY_GROUP, useTime, startDate, endDate, account, info.getVmId(), info.getSGId(), info.getZoneId()); + } + } + + return true; + } + + private static void updateSGUsageData(Map> usageDataMap, String key, long vmId, long duration) { + Pair sgUsageInfo = usageDataMap.get(key); + if (sgUsageInfo == null) { + sgUsageInfo = new Pair(new Long(vmId), new Long(duration)); + } else { + Long runningTime = sgUsageInfo.second(); + runningTime = new Long(runningTime.longValue() + duration); + sgUsageInfo = new Pair(sgUsageInfo.first(), runningTime); + } + usageDataMap.put(key, sgUsageInfo); + } + + private static void createUsageRecord(int type, long runningTime, Date startDate, Date endDate, AccountVO account, long vmId, long sgId, long zoneId) { + // Our smallest increment is hourly for now + if (s_logger.isDebugEnabled()) { + s_logger.debug("Total running time " + runningTime + "ms"); + } + + float usage = runningTime / 1000f / 60f / 60f; + + DecimalFormat dFormat = new DecimalFormat("#.######"); + String usageDisplay = dFormat.format(usage); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Creating security group:" + sgId + " usage record for Vm : " + vmId + ", usage: " + usageDisplay + ", startDate: " + startDate + ", endDate: " + endDate + ", for account: " + account.getId()); + } + + // Create the usage record + String usageDesc = "Security Group: " + sgId + " for Vm : " + vmId + " usage time"; + + UsageVO usageRecord = new UsageVO(zoneId, account.getId(), account.getDomainId(), usageDesc, usageDisplay + " Hrs", type, + new Double(usage), vmId, null, null, null, sgId, null, startDate, endDate); + m_usageDao.persist(usageRecord); + } + + private static class SGInfo { + private long vmId; + private long zoneId; + private long sgId; + + public SGInfo(long vmId, long zoneId, long sgId) { + this.vmId = vmId; + this.zoneId = zoneId; + this.sgId = sgId; + } + public long getZoneId() { + return zoneId; + } + public long getVmId() { + return vmId; + } + public long getSGId() { + return sgId; + } + } + +}