Skip to content
44 changes: 27 additions & 17 deletions api/src/main/java/org/apache/cloudstack/alert/AlertService.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,24 @@

public interface AlertService {
public static class AlertType {
private static Set<AlertType> defaultAlertTypes = new HashSet<AlertType>();
private static final Set<AlertType> defaultAlertTypes = new HashSet<>();
private final String name;
private final short type;
private final boolean repetitionAllowed;

private AlertType(short type, String name, boolean isDefault) {
private AlertType(short type, String name, boolean isDefault, boolean repetitionAllowed) {
this.name = name;
this.type = type;
this.repetitionAllowed = repetitionAllowed;
if (isDefault) {
defaultAlertTypes.add(this);
}
}

private AlertType(short type, String name, boolean isDefault) {
this(type, name, isDefault, false);
}

public static final AlertType ALERT_TYPE_MEMORY = new AlertType(Capacity.CAPACITY_TYPE_MEMORY, "ALERT.MEMORY", true);
public static final AlertType ALERT_TYPE_CPU = new AlertType(Capacity.CAPACITY_TYPE_CPU, "ALERT.CPU", true);
public static final AlertType ALERT_TYPE_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_STORAGE, "ALERT.STORAGE", true);
Expand All @@ -45,36 +51,36 @@ private AlertType(short type, String name, boolean isDefault) {
public static final AlertType ALERT_TYPE_VIRTUAL_NETWORK_IPV6_SUBNET = new AlertType(Capacity.CAPACITY_TYPE_VIRTUAL_NETWORK_IPV6_SUBNET, "ALERT.NETWORK.IPV6SUBNET", true);
public static final AlertType ALERT_TYPE_PRIVATE_IP = new AlertType(Capacity.CAPACITY_TYPE_PRIVATE_IP, "ALERT.NETWORK.PRIVATEIP", true);
public static final AlertType ALERT_TYPE_SECONDARY_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_SECONDARY_STORAGE, "ALERT.STORAGE.SECONDARY", true);
public static final AlertType ALERT_TYPE_HOST = new AlertType((short)7, "ALERT.COMPUTE.HOST", true);
public static final AlertType ALERT_TYPE_USERVM = new AlertType((short)8, "ALERT.USERVM", true);
public static final AlertType ALERT_TYPE_DOMAIN_ROUTER = new AlertType((short)9, "ALERT.SERVICE.DOMAINROUTER", true);
public static final AlertType ALERT_TYPE_CONSOLE_PROXY = new AlertType((short)10, "ALERT.SERVICE.CONSOLEPROXY", true);
public static final AlertType ALERT_TYPE_HOST = new AlertType((short)7, "ALERT.COMPUTE.HOST", true, true);
public static final AlertType ALERT_TYPE_USERVM = new AlertType((short)8, "ALERT.USERVM", true, true);
public static final AlertType ALERT_TYPE_DOMAIN_ROUTER = new AlertType((short)9, "ALERT.SERVICE.DOMAINROUTER", true, true);
public static final AlertType ALERT_TYPE_CONSOLE_PROXY = new AlertType((short)10, "ALERT.SERVICE.CONSOLEPROXY", true, true);
public static final AlertType ALERT_TYPE_ROUTING = new AlertType((short)11, "ALERT.NETWORK.ROUTING", true);
public static final AlertType ALERT_TYPE_STORAGE_MISC = new AlertType((short)12, "ALERT.STORAGE.MISC", true);
public static final AlertType ALERT_TYPE_STORAGE_MISC = new AlertType((short)12, "ALERT.STORAGE.MISC", true, true);
public static final AlertType ALERT_TYPE_USAGE_SERVER = new AlertType((short)13, "ALERT.USAGE", true);
public static final AlertType ALERT_TYPE_MANAGEMENT_NODE = new AlertType((short)14, "ALERT.MANAGEMENT", true);
public static final AlertType ALERT_TYPE_MANAGEMENT_NODE = new AlertType((short)14, "ALERT.MANAGEMENT", true, true);
public static final AlertType ALERT_TYPE_DOMAIN_ROUTER_MIGRATE = new AlertType((short)15, "ALERT.NETWORK.DOMAINROUTERMIGRATE", true);
public static final AlertType ALERT_TYPE_CONSOLE_PROXY_MIGRATE = new AlertType((short)16, "ALERT.SERVICE.CONSOLEPROXYMIGRATE", true);
public static final AlertType ALERT_TYPE_USERVM_MIGRATE = new AlertType((short)17, "ALERT.USERVM.MIGRATE", true);
public static final AlertType ALERT_TYPE_VLAN = new AlertType((short)18, "ALERT.NETWORK.VLAN", true);
public static final AlertType ALERT_TYPE_SSVM = new AlertType((short)19, "ALERT.SERVICE.SSVM", true);
public static final AlertType ALERT_TYPE_SSVM = new AlertType((short)19, "ALERT.SERVICE.SSVM", true, true);
public static final AlertType ALERT_TYPE_USAGE_SERVER_RESULT = new AlertType((short)20, "ALERT.USAGE.RESULT", true);
public static final AlertType ALERT_TYPE_STORAGE_DELETE = new AlertType((short)21, "ALERT.STORAGE.DELETE", true);
public static final AlertType ALERT_TYPE_UPDATE_RESOURCE_COUNT = new AlertType((short)22, "ALERT.RESOURCE.COUNT", true);
public static final AlertType ALERT_TYPE_USAGE_SANITY_RESULT = new AlertType((short)23, "ALERT.USAGE.SANITY", true);
public static final AlertType ALERT_TYPE_DIRECT_ATTACHED_PUBLIC_IP = new AlertType((short)24, "ALERT.NETWORK.DIRECTPUBLICIP", true);
public static final AlertType ALERT_TYPE_LOCAL_STORAGE = new AlertType((short)25, "ALERT.STORAGE.LOCAL", true);
public static final AlertType ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED = new AlertType((short)26, "ALERT.RESOURCE.EXCEED", true);
public static final AlertType ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED = new AlertType((short)26, "ALERT.RESOURCE.EXCEED", true, true);
public static final AlertType ALERT_TYPE_SYNC = new AlertType((short)27, "ALERT.TYPE.SYNC", true);
public static final AlertType ALERT_TYPE_UPLOAD_FAILED = new AlertType((short)28, "ALERT.UPLOAD.FAILED", true);
public static final AlertType ALERT_TYPE_OOBM_AUTH_ERROR = new AlertType((short)29, "ALERT.OOBM.AUTHERROR", true);
public static final AlertType ALERT_TYPE_HA_ACTION = new AlertType((short)30, "ALERT.HA.ACTION", true);
public static final AlertType ALERT_TYPE_CA_CERT = new AlertType((short)31, "ALERT.CA.CERT", true);
public static final AlertType ALERT_TYPE_UPLOAD_FAILED = new AlertType((short)28, "ALERT.UPLOAD.FAILED", true, true);
public static final AlertType ALERT_TYPE_OOBM_AUTH_ERROR = new AlertType((short)29, "ALERT.OOBM.AUTHERROR", true, true);
public static final AlertType ALERT_TYPE_HA_ACTION = new AlertType((short)30, "ALERT.HA.ACTION", true, true);
public static final AlertType ALERT_TYPE_CA_CERT = new AlertType((short)31, "ALERT.CA.CERT", true, true);
public static final AlertType ALERT_TYPE_VM_SNAPSHOT = new AlertType((short)32, "ALERT.VM.SNAPSHOT", true);
public static final AlertType ALERT_TYPE_VR_PUBLIC_IFACE_MTU = new AlertType((short)33, "ALERT.VR.PUBLIC.IFACE.MTU", true);
public static final AlertType ALERT_TYPE_VR_PRIVATE_IFACE_MTU = new AlertType((short)34, "ALERT.VR.PRIVATE.IFACE.MTU", true);
public static final AlertType ALERT_TYPE_EXTENSION_PATH_NOT_READY = new AlertType((short)33, "ALERT.TYPE.EXTENSION.PATH.NOT.READY", true);
public static final AlertType ALERT_TYPE_VPN_GATEWAY_OBSOLETE_PARAMETERS = new AlertType((short)34, "ALERT.S2S.VPN.GATEWAY.OBSOLETE.PARAMETERS", true);
public static final AlertType ALERT_TYPE_EXTENSION_PATH_NOT_READY = new AlertType((short)33, "ALERT.TYPE.EXTENSION.PATH.NOT.READY", true, true);
public static final AlertType ALERT_TYPE_VPN_GATEWAY_OBSOLETE_PARAMETERS = new AlertType((short)34, "ALERT.S2S.VPN.GATEWAY.OBSOLETE.PARAMETERS", true, true);
public static final AlertType ALERT_TYPE_BACKUP_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_BACKUP_STORAGE, "ALERT.STORAGE.BACKUP", true);
public static final AlertType ALERT_TYPE_OBJECT_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_OBJECT_STORAGE, "ALERT.STORAGE.OBJECT", true);

Expand All @@ -86,6 +92,10 @@ public String getName() {
return name;
}

public boolean isRepetitionAllowed() {
return repetitionAllowed;
}

private static AlertType getAlertType(short type) {
for (AlertType alertType : defaultAlertTypes) {
if (alertType.getType() == type) {
Expand All @@ -109,7 +119,7 @@ public static AlertType generateAlert(short type, String name) {
if (defaultAlert != null && !defaultAlert.getName().equalsIgnoreCase(name)) {
throw new InvalidParameterValueException("There is a default alert having type " + type + " and name " + defaultAlert.getName());
} else {
return new AlertType(type, name, false);
return new AlertType(type, name, false, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so custom alerts are never allowed to be repeated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latest changes will allow that if operator has added the type name in the config,

image

}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ public class ApiConstants {
public static final String RECONNECT = "reconnect";
public static final String RECOVER = "recover";
public static final String REPAIR = "repair";
public static final String REPETITION_ALLOWED = "repetitionallowed";
public static final String REQUIRES_HVM = "requireshvm";
public static final String RESOURCES = "resources";
public static final String RESOURCE_COUNT = "resourcecount";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@
// under the License.
package org.apache.cloudstack.api.command.admin.resource;

import com.cloud.user.Account;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.response.AlertResponse;
import org.apache.cloudstack.api.response.AlertTypeResponse;
import org.apache.cloudstack.api.response.ListResponse;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.cloud.user.Account;

@APICommand(name = "listAlertTypes", description = "Lists all alerts types", responseObject = AlertResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
Expand All @@ -43,7 +44,8 @@ public void execute() {
ListResponse<AlertTypeResponse> response = new ListResponse<>();
List<AlertTypeResponse> typeResponseList = new ArrayList<>();
for (AlertService.AlertType alertType : result) {
AlertTypeResponse alertResponse = new AlertTypeResponse(alertType.getType(), alertType.getName());
AlertTypeResponse alertResponse = new AlertTypeResponse(alertType.getType(), alertType.getName(),
alertType.isRepetitionAllowed());
alertResponse.setObjectName("alerttype");
typeResponseList.add(alertResponse);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
// under the License.
package org.apache.cloudstack.api.response;

import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;

import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;

public class AlertTypeResponse extends BaseResponse {

@SerializedName("alerttypeid")
Expand All @@ -31,6 +32,10 @@ public class AlertTypeResponse extends BaseResponse {
@Param(description = "description of alert type")
private String name;

@SerializedName(ApiConstants.REPETITION_ALLOWED)
@Param(description = "Whether repetitive alerts allowed for the alert type", since = "4.22.0")
private boolean repetitionAllowed = true;

public String getName() {
return name;
}
Expand All @@ -47,9 +52,10 @@ public void setUsageType(short alertType) {
this.alertType = alertType;
}

public AlertTypeResponse(short alertType, String name) {
public AlertTypeResponse(short alertType, String name, boolean repetitionAllowed) {
this.alertType = alertType;
this.name = name;
this.repetitionAllowed = repetitionAllowed;
setObjectName("alerttype");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ public interface AlertManager extends Manager, AlertService {
"Percentage (as a value between 0 and 1) of guest network IPv6 subnet utilization above which alerts will be sent.",
true);

ConfigKey<String> AllowedRepetitiveAlertTypes = new ConfigKey<>(ConfigKey.CATEGORY_ALERT, String.class,
"alert.allowed.repetitive.types", "",
"Comma-separated list of alert types (by name) that can be sent multiple times", true);

void clearAlert(AlertType alertType, long dataCenterId, long podId);

void recalculateCapacity();
Expand Down
72 changes: 47 additions & 25 deletions server/src/main/java/com/cloud/alert/AlertManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.io.UnsupportedEncodingException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -37,15 +36,12 @@
import javax.mail.MessagingException;
import javax.naming.ConfigurationException;

import com.cloud.dc.DataCenter;
import com.cloud.dc.Pod;
import com.cloud.org.Cluster;

import org.apache.cloudstack.backup.BackupManager;
import org.apache.cloudstack.framework.config.ConfigDepot;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.messagebus.MessageBus;
import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
import org.apache.cloudstack.storage.datastore.db.ObjectStoreDao;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
Expand All @@ -54,6 +50,7 @@
import org.apache.cloudstack.utils.mailing.SMTPMailProperties;
import org.apache.cloudstack.utils.mailing.SMTPMailSender;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand All @@ -70,9 +67,11 @@
import com.cloud.configuration.Config;
import com.cloud.configuration.ConfigurationManager;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenter.NetworkType;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.HostPodVO;
import com.cloud.dc.Pod;
import com.cloud.dc.Vlan.VlanType;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
Expand All @@ -86,10 +85,12 @@
import com.cloud.host.dao.HostDao;
import com.cloud.network.Ipv6Service;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.org.Cluster;
import com.cloud.org.Grouping.AllocationState;
import com.cloud.resource.ResourceManager;
import com.cloud.storage.StorageManager;
import com.cloud.utils.Pair;
import com.cloud.utils.Ternary;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.SearchCriteria;
Expand All @@ -100,21 +101,6 @@
public class AlertManagerImpl extends ManagerBase implements AlertManager, Configurable {
protected Logger logger = LogManager.getLogger(AlertManagerImpl.class.getName());

public static final List<AlertType> ALERTS = Arrays.asList(AlertType.ALERT_TYPE_HOST
, AlertType.ALERT_TYPE_USERVM
, AlertType.ALERT_TYPE_DOMAIN_ROUTER
, AlertType.ALERT_TYPE_CONSOLE_PROXY
, AlertType.ALERT_TYPE_SSVM
, AlertType.ALERT_TYPE_STORAGE_MISC
, AlertType.ALERT_TYPE_MANAGEMENT_NODE
, AlertType.ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED
, AlertType.ALERT_TYPE_UPLOAD_FAILED
, AlertType.ALERT_TYPE_OOBM_AUTH_ERROR
, AlertType.ALERT_TYPE_HA_ACTION
, AlertType.ALERT_TYPE_CA_CERT
, AlertType.ALERT_TYPE_EXTENSION_PATH_NOT_READY
, AlertType.ALERT_TYPE_VPN_GATEWAY_OBSOLETE_PARAMETERS);

private static final long INITIAL_CAPACITY_CHECK_DELAY = 30L * 1000L; // Thirty seconds expressed in milliseconds.

private static final DecimalFormat DfPct = new DecimalFormat("###.##");
Expand Down Expand Up @@ -156,6 +142,8 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
Ipv6Service ipv6Service;
@Inject
HostDao hostDao;
@Inject
MessageBus messageBus;

private Timer _timer = null;
private long _capacityCheckPeriod = 60L * 60L * 1000L; // One hour by default.
Expand All @@ -175,6 +163,8 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
protected String[] recipients = null;
protected String senderAddress = null;

private final List<String> allowedRepetitiveAlertTypeNames = new ArrayList<>();

public AlertManagerImpl() {
_executor = Executors.newCachedThreadPool(new NamedThreadFactory("Email-Alerts-Sender"));
}
Expand Down Expand Up @@ -254,12 +244,32 @@ public boolean configure(String name, Map<String, Object> params) throws Configu
_capacityCheckPeriod = Long.parseLong(Config.CapacityCheckPeriod.getDefaultValue());
}
}
initMessageBusListener();
setupRepetitiveAlertTypes();

_timer = new Timer("CapacityChecker");

return true;
}

protected void setupRepetitiveAlertTypes() {
allowedRepetitiveAlertTypeNames.clear();
String allowedRepetitiveAlertsStr = AllowedRepetitiveAlertTypes.value();
logger.trace("Allowed repetitive alert types specified by {}: {} ", AllowedRepetitiveAlertTypes.key(),
allowedRepetitiveAlertsStr);
if (StringUtils.isBlank(allowedRepetitiveAlertsStr)) {
return;
}
String[] allowedRepetitiveAlertTypesArray = allowedRepetitiveAlertsStr.split(",");
for (String allowedTypeName : allowedRepetitiveAlertTypesArray) {
if (StringUtils.isBlank(allowedTypeName)) {
continue;
}
allowedRepetitiveAlertTypeNames.add(allowedTypeName.toLowerCase());
}
logger.trace("{} alert types specified for repetitive alerts", allowedRepetitiveAlertTypeNames.size());
}

@Override
public boolean start() {
_timer.schedule(new CapacityChecker(), INITIAL_CAPACITY_CHECK_DELAY, _capacityCheckPeriod);
Expand Down Expand Up @@ -850,11 +860,11 @@ public void sendAlert(AlertType alertType, DataCenter dataCenter, Pod pod, Clust

@Nullable
private AlertVO getAlertForTrivialAlertType(AlertType alertType, long dataCenterId, Long podId, Long clusterId) {
AlertVO alert = null;
if (!ALERTS.contains(alertType)) {
alert = _alertDao.getLastAlert(alertType.getType(), dataCenterId, podId, clusterId);
if (alertType.isRepetitionAllowed() || (StringUtils.isNotBlank(alertType.getName()) &&
allowedRepetitiveAlertTypeNames.contains(alertType.getName().toLowerCase()))) {
return null;
}
return alert;
return _alertDao.getLastAlert(alertType.getType(), dataCenterId, podId, clusterId);
}

protected void sendMessage(SMTPMailProperties mailProps) {
Expand Down Expand Up @@ -883,7 +893,7 @@ public String getConfigComponentName() {
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {CPUCapacityThreshold, MemoryCapacityThreshold, StorageAllocatedCapacityThreshold, StorageCapacityThreshold, AlertSmtpEnabledSecurityProtocols,
AlertSmtpUseStartTLS, Ipv6SubnetCapacityThreshold, AlertSmtpUseAuth};
AlertSmtpUseStartTLS, Ipv6SubnetCapacityThreshold, AlertSmtpUseAuth, AllowedRepetitiveAlertTypes};
}

@Override
Expand All @@ -897,4 +907,16 @@ public boolean generateAlert(AlertType alertType, long dataCenterId, Long podId,
return false;
}
}

@SuppressWarnings("unchecked")
protected void initMessageBusListener() {
messageBus.subscribe(EventTypes.EVENT_CONFIGURATION_VALUE_EDIT, (senderAddress, subject, args) -> {
Ternary<String, ConfigKey.Scope, Long> updatedSetting = (Ternary<String, ConfigKey.Scope, Long>) args;
String updatedSettingName = updatedSetting.first();
if (!AllowedRepetitiveAlertTypes.key().equals(updatedSettingName)) {
return;
}
setupRepetitiveAlertTypes();
});
}
}
Loading
Loading