Merge pull request #12616 from AndriiLandiak/notifications-on-resource-shortage
Notification on resources shortage
This commit is contained in:
commit
f65fa52c29
@ -116,6 +116,7 @@ public class ThingsboardInstallService {
|
||||
entityDatabaseSchemaService.createDatabaseIndexes();
|
||||
|
||||
// TODO: cleanup update code after each release
|
||||
systemDataLoaderService.updateDefaultNotificationConfigs(false);
|
||||
|
||||
// Runs upgrade scripts that are not possible in plain SQL.
|
||||
dataUpdateService.updateData();
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.service.notification.rule.trigger;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.server.common.data.notification.info.ResourcesShortageNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.ResourcesShortageNotificationRuleTriggerConfig;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ResourcesShortageTriggerProcessor implements NotificationRuleTriggerProcessor<ResourcesShortageTrigger, ResourcesShortageNotificationRuleTriggerConfig> {
|
||||
|
||||
@Override
|
||||
public boolean matchesFilter(ResourcesShortageTrigger trigger, ResourcesShortageNotificationRuleTriggerConfig triggerConfig) {
|
||||
float usagePercent = trigger.getUsage() / 100.0f;
|
||||
return switch (trigger.getResource()) {
|
||||
case CPU -> usagePercent >= triggerConfig.getCpuThreshold();
|
||||
case RAM -> usagePercent >= triggerConfig.getRamThreshold();
|
||||
case STORAGE -> usagePercent >= triggerConfig.getStorageThreshold();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuleOriginatedNotificationInfo constructNotificationInfo(ResourcesShortageTrigger trigger) {
|
||||
return ResourcesShortageNotificationInfo.builder().resource(trigger.getResource().name()).usage(trigger.getUsage()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationRuleTriggerType getTriggerType() {
|
||||
return NotificationRuleTriggerType.RESOURCES_SHORTAGE;
|
||||
}
|
||||
|
||||
}
|
||||
@ -39,6 +39,9 @@ import org.thingsboard.server.common.data.kv.BooleanDataEntry;
|
||||
import org.thingsboard.server.common.data.kv.JsonDataEntry;
|
||||
import org.thingsboard.server.common.data.kv.LongDataEntry;
|
||||
import org.thingsboard.server.common.data.kv.TsKvEntry;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger.Resource;
|
||||
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
|
||||
import org.thingsboard.server.common.msg.queue.ServiceType;
|
||||
import org.thingsboard.server.common.stats.TbApiUsageStateClient;
|
||||
import org.thingsboard.server.dao.domain.DomainService;
|
||||
@ -92,6 +95,7 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
|
||||
private final DomainService domainService;
|
||||
private final MailService mailService;
|
||||
private final SmsService smsService;
|
||||
private final NotificationRuleProcessor notificationRuleProcessor;
|
||||
private volatile ScheduledExecutorService scheduler;
|
||||
|
||||
@Value("${metrics.system_info.persist_frequency:60}")
|
||||
@ -163,7 +167,7 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
|
||||
if (twoFaSettings != null) {
|
||||
var providers = twoFaSettings.getJsonValue().get("providers");
|
||||
if (providers != null) {
|
||||
return providers.size() > 0;
|
||||
return !providers.isEmpty();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -180,6 +184,11 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
|
||||
private void saveCurrentClusterSystemInfo() {
|
||||
long ts = System.currentTimeMillis();
|
||||
List<SystemInfoData> clusterSystemData = getSystemData(serviceInfoProvider.getServiceInfo());
|
||||
clusterSystemData.forEach(data -> {
|
||||
notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(data.getCpuUsage()).build());
|
||||
notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(data.getMemoryUsage()).build());
|
||||
notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(data.getDiscUsage()).build());
|
||||
});
|
||||
BasicTsKvEntry clusterDataKv = new BasicTsKvEntry(ts, new JsonDataEntry("clusterSystemData", JacksonUtil.toString(clusterSystemData)));
|
||||
doSave(Arrays.asList(new BasicTsKvEntry(ts, new BooleanDataEntry("clusterMode", true)), clusterDataKv));
|
||||
}
|
||||
@ -188,9 +197,21 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
|
||||
long ts = System.currentTimeMillis();
|
||||
List<TsKvEntry> tsList = new ArrayList<>();
|
||||
tsList.add(new BasicTsKvEntry(ts, new BooleanDataEntry("clusterMode", false)));
|
||||
getCpuUsage().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuUsage", (long) v))));
|
||||
getMemoryUsage().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("memoryUsage", (long) v))));
|
||||
getDiscSpaceUsage().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("discUsage", (long) v))));
|
||||
getCpuUsage().ifPresent(v -> {
|
||||
long value = (long) v;
|
||||
tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuUsage", value)));
|
||||
notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(value).build());
|
||||
});
|
||||
getMemoryUsage().ifPresent(v -> {
|
||||
long value = (long) v;
|
||||
tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("memoryUsage", value)));
|
||||
notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(value).build());
|
||||
});
|
||||
getDiscSpaceUsage().ifPresent(v -> {
|
||||
long value = (long) v;
|
||||
tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("discUsage", value)));
|
||||
notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(value).build());
|
||||
});
|
||||
|
||||
getCpuCount().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuCount", (long) v))));
|
||||
getTotalMemory().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("totalMemory", v))));
|
||||
@ -244,4 +265,5 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
|
||||
scheduler = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -68,6 +68,8 @@ import org.thingsboard.server.common.data.notification.rule.NotificationRule;
|
||||
import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionTrigger;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.RateLimitsTrigger;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger.Resource;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmCommentNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig;
|
||||
@ -78,6 +80,7 @@ import org.thingsboard.server.common.data.notification.rule.trigger.config.Entit
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.NewPlatformVersionNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.RateLimitsNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.ResourcesShortageNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
|
||||
import org.thingsboard.server.common.data.notification.targets.platform.AffectedTenantAdministratorsFilter;
|
||||
import org.thingsboard.server.common.data.notification.targets.platform.SystemAdministratorsFilter;
|
||||
@ -91,6 +94,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
|
||||
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
|
||||
import org.thingsboard.server.common.data.security.Authority;
|
||||
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
|
||||
import org.thingsboard.server.controller.TbTestWebSocketClient;
|
||||
import org.thingsboard.server.dao.notification.DefaultNotifications;
|
||||
import org.thingsboard.server.dao.notification.NotificationRequestService;
|
||||
import org.thingsboard.server.dao.rule.RuleChainService;
|
||||
@ -98,8 +102,10 @@ import org.thingsboard.server.dao.service.DaoSqlTest;
|
||||
import org.thingsboard.server.queue.notification.DefaultNotificationDeduplicationService;
|
||||
import org.thingsboard.server.service.notification.rule.cache.DefaultNotificationRulesCache;
|
||||
import org.thingsboard.server.service.state.DeviceStateService;
|
||||
import org.thingsboard.server.service.system.DefaultSystemInfoService;
|
||||
import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -134,6 +140,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
|
||||
@SpyBean
|
||||
private AlarmSubscriptionService alarmSubscriptionService;
|
||||
@Autowired
|
||||
private DefaultSystemInfoService systemInfoService;
|
||||
@Autowired
|
||||
private NotificationRequestService notificationRequestService;
|
||||
@Autowired
|
||||
private RateLimitService rateLimitService;
|
||||
@ -254,7 +262,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
|
||||
assertThat(info.getAlarmStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK);
|
||||
});
|
||||
|
||||
clients.values().forEach(wsClient -> wsClient.registerWaitForUpdate());
|
||||
clients.values().forEach(TbTestWebSocketClient::registerWaitForUpdate);
|
||||
alarmSubscriptionService.acknowledgeAlarm(tenantId, alarm.getId(), System.currentTimeMillis());
|
||||
AlarmStatus expectedStatus = AlarmStatus.ACTIVE_ACK;
|
||||
AlarmSeverity expectedSeverity = AlarmSeverity.CRITICAL;
|
||||
@ -638,7 +646,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
|
||||
rule = saveNotificationRule(rule);
|
||||
|
||||
NotificationRuleInfo ruleInfo = findNotificationRules().getData().get(0);
|
||||
assertThat(ruleInfo.getId()).isEqualTo(ruleInfo.getId());
|
||||
assertThat(ruleInfo.getId()).isEqualTo(rule.getId());
|
||||
assertThat(ruleInfo.getTemplateName()).isEqualTo(template.getName());
|
||||
assertThat(ruleInfo.getDeliveryMethods()).containsOnly(deliveryMethods);
|
||||
}
|
||||
@ -780,6 +788,59 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotificationRuleProcessing_resourcesShortage() throws Exception {
|
||||
loginSysAdmin();
|
||||
ResourcesShortageNotificationRuleTriggerConfig triggerConfig = ResourcesShortageNotificationRuleTriggerConfig.builder()
|
||||
.ramThreshold(0.01f)
|
||||
.cpuThreshold(1f)
|
||||
.storageThreshold(1f)
|
||||
.build();
|
||||
createNotificationRule(triggerConfig, "Warning: ${resource} shortage", "${resource} shortage", createNotificationTarget(tenantAdminUserId).getId());
|
||||
loginTenantAdmin();
|
||||
|
||||
Method method = DefaultSystemInfoService.class.getDeclaredMethod("saveCurrentMonolithSystemInfo");
|
||||
method.setAccessible(true);
|
||||
method.invoke(systemInfoService);
|
||||
|
||||
await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
|
||||
Notification notification = getMyNotifications(false, 100).get(0);
|
||||
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
|
||||
assertThat(notification.getText()).isEqualTo("RAM shortage");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotificationsDeduplication_resourcesShortage() throws Exception {
|
||||
loginSysAdmin();
|
||||
ResourcesShortageNotificationRuleTriggerConfig triggerConfig = ResourcesShortageNotificationRuleTriggerConfig.builder()
|
||||
.ramThreshold(0.01f)
|
||||
.cpuThreshold(1f)
|
||||
.storageThreshold(1f)
|
||||
.build();
|
||||
createNotificationRule(triggerConfig, "Warning: ${resource} shortage", "${resource} shortage", createNotificationTarget(tenantAdminUserId).getId());it a
|
||||
loginTenantAdmin();
|
||||
|
||||
assertThat(getMyNotifications(false, 100)).size().isZero();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
notificationRuleProcessor.process(ResourcesShortageTrigger.builder()
|
||||
.resource(Resource.RAM)
|
||||
.usage(15L)
|
||||
.build());
|
||||
TimeUnit.MILLISECONDS.sleep(300);
|
||||
}
|
||||
await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
|
||||
Notification notification = getMyNotifications(false, 100).get(0);
|
||||
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
|
||||
assertThat(notification.getText()).isEqualTo("RAM shortage");
|
||||
|
||||
// deduplication is 5 minute, no new message is exp
|
||||
notificationRuleProcessor.process(ResourcesShortageTrigger.builder()
|
||||
.resource(Resource.RAM)
|
||||
.usage(5L)
|
||||
.build());
|
||||
await("").atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotificationRuleDisabling() throws Exception {
|
||||
EntityActionNotificationRuleTriggerConfig triggerConfig = new EntityActionNotificationRuleTriggerConfig();
|
||||
|
||||
@ -37,7 +37,8 @@ public enum NotificationType {
|
||||
RATE_LIMITS,
|
||||
EDGE_CONNECTION,
|
||||
EDGE_COMMUNICATION_FAILURE,
|
||||
TASK_PROCESSING_FAILURE;
|
||||
TASK_PROCESSING_FAILURE,
|
||||
RESOURCES_SHORTAGE;
|
||||
|
||||
@Getter
|
||||
private boolean system; // for future use and compatibility with PE
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.common.data.notification.info;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ResourcesShortageNotificationInfo implements RuleOriginatedNotificationInfo {
|
||||
|
||||
private String resource;
|
||||
private Long usage;
|
||||
|
||||
@Override
|
||||
public Map<String, String> getTemplateData() {
|
||||
return Map.of(
|
||||
"resource", resource,
|
||||
"usage", String.valueOf(usage)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -55,9 +55,4 @@ public class NewPlatformVersionTrigger implements NotificationRuleTrigger {
|
||||
updateInfo.getCurrentVersion(), updateInfo.getLatestVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDefaultDeduplicationDuration() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.common.data.notification.rule.trigger;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class ResourcesShortageTrigger implements NotificationRuleTrigger {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 6024216015202949570L;
|
||||
|
||||
private Resource resource;
|
||||
private Long usage;
|
||||
|
||||
@Override
|
||||
public TenantId getTenantId() {
|
||||
return TenantId.SYS_TENANT_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityId getOriginatorEntityId() {
|
||||
return TenantId.SYS_TENANT_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deduplicate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeduplicationKey() {
|
||||
return resource.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDefaultDeduplicationDuration() {
|
||||
return TimeUnit.HOURS.toMillis(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationRuleTriggerType getType() {
|
||||
return NotificationRuleTriggerType.RESOURCES_SHORTAGE;
|
||||
}
|
||||
|
||||
public enum Resource {
|
||||
CPU, RAM, STORAGE
|
||||
}
|
||||
|
||||
}
|
||||
@ -22,10 +22,15 @@ import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class TaskProcessingFailureTrigger implements NotificationRuleTrigger {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 5606203770553105345L;
|
||||
|
||||
private final HousekeeperTask task;
|
||||
private final int attempt;
|
||||
private final Throwable error;
|
||||
@ -45,9 +50,4 @@ public class TaskProcessingFailureTrigger implements NotificationRuleTrigger {
|
||||
return task.getEntityId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deduplicate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -38,7 +38,8 @@ import java.io.Serializable;
|
||||
@Type(value = RateLimitsNotificationRuleTriggerConfig.class, name = "RATE_LIMITS"),
|
||||
@Type(value = EdgeConnectionNotificationRuleTriggerConfig.class, name = "EDGE_CONNECTION"),
|
||||
@Type(value = EdgeCommunicationFailureNotificationRuleTriggerConfig.class, name = "EDGE_COMMUNICATION_FAILURE"),
|
||||
@Type(value = TaskProcessingFailureNotificationRuleTriggerConfig.class, name = "TASK_PROCESSING_FAILURE")
|
||||
@Type(value = TaskProcessingFailureNotificationRuleTriggerConfig.class, name = "TASK_PROCESSING_FAILURE"),
|
||||
@Type(value = ResourcesShortageNotificationRuleTriggerConfig.class, name = "RESOURCES_SHORTAGE")
|
||||
})
|
||||
public interface NotificationRuleTriggerConfig extends Serializable {
|
||||
|
||||
|
||||
@ -32,7 +32,8 @@ public enum NotificationRuleTriggerType {
|
||||
ENTITIES_LIMIT(false),
|
||||
API_USAGE_LIMIT(false),
|
||||
RATE_LIMITS(false),
|
||||
TASK_PROCESSING_FAILURE(false);
|
||||
TASK_PROCESSING_FAILURE(false),
|
||||
RESOURCES_SHORTAGE(false);
|
||||
|
||||
private final boolean tenantLevel;
|
||||
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.common.data.notification.rule.trigger.config;
|
||||
|
||||
import jakarta.validation.constraints.Max;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class ResourcesShortageNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 339395299693241424L;
|
||||
|
||||
@Max(1)
|
||||
private float cpuThreshold; // in percents
|
||||
@Max(1)
|
||||
private float ramThreshold; // in percents
|
||||
@Max(1)
|
||||
private float storageThreshold; // in percents
|
||||
|
||||
@Override
|
||||
public NotificationRuleTriggerType getTriggerType() {
|
||||
return NotificationRuleTriggerType.RESOURCES_SHORTAGE;
|
||||
}
|
||||
|
||||
}
|
||||
@ -186,6 +186,7 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
|
||||
defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimitsForSysadmin, sysAdmins.getId());
|
||||
defaultNotifications.create(tenantId, DefaultNotifications.newPlatformVersion, sysAdmins.getId());
|
||||
defaultNotifications.create(tenantId, DefaultNotifications.taskProcessingFailure, tenantAdmins.getId());
|
||||
defaultNotifications.create(tenantId, DefaultNotifications.resourcesShortage, sysAdmins.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -221,6 +222,9 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
|
||||
if (!isNotificationConfigured(tenantId, NotificationType.TASK_PROCESSING_FAILURE)) {
|
||||
defaultNotifications.create(tenantId, DefaultNotifications.taskProcessingFailure, sysAdmins.getId());
|
||||
}
|
||||
if (!isNotificationConfigured(tenantId, NotificationType.RESOURCES_SHORTAGE)) {
|
||||
defaultNotifications.create(tenantId, DefaultNotifications.resourcesShortage, sysAdmins.getId());
|
||||
}
|
||||
} else {
|
||||
var requiredNotificationTypes = List.of(NotificationType.EDGE_CONNECTION, NotificationType.EDGE_COMMUNICATION_FAILURE);
|
||||
var existingNotificationTypes = notificationTemplateService.findNotificationTemplatesByTenantIdAndNotificationTypes(
|
||||
|
||||
@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.notification.rule.DefaultNotificationR
|
||||
import org.thingsboard.server.common.data.notification.rule.EscalatedNotificationRuleRecipientsConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.NotificationRule;
|
||||
import org.thingsboard.server.common.data.notification.rule.NotificationRuleConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger.Resource;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmCommentNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig;
|
||||
@ -49,6 +50,7 @@ import org.thingsboard.server.common.data.notification.rule.trigger.config.NewPl
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.RateLimitsNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.ResourcesShortageNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.TaskProcessingFailureNotificationRuleTriggerConfig;
|
||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
|
||||
@ -372,6 +374,20 @@ public class DefaultNotifications {
|
||||
.build())
|
||||
.build();
|
||||
|
||||
public static final DefaultNotification resourcesShortage = DefaultNotification.builder()
|
||||
.name("Resources shortage notification")
|
||||
.type(NotificationType.RESOURCES_SHORTAGE)
|
||||
.subject("Warning: ${resource} shortage")
|
||||
.text("${resource} usage is at ${usage}%.")
|
||||
.icon("warning")
|
||||
.rule(DefaultRule.builder()
|
||||
.name("Resources shortage")
|
||||
.triggerConfig(ResourcesShortageNotificationRuleTriggerConfig.builder().cpuThreshold(0.8f).storageThreshold(0.8f).ramThreshold(0.8f).build())
|
||||
.description("Send notification to system admins on resource shortage")
|
||||
.build())
|
||||
.color(RED_COLOR)
|
||||
.build();
|
||||
|
||||
private final NotificationTemplateService templateService;
|
||||
private final NotificationRuleService ruleService;
|
||||
|
||||
|
||||
@ -480,17 +480,18 @@
|
||||
ignoreAuthorityFilter
|
||||
[allowedEntityTypes]="allowEntityTypeForEntitiesLimit">
|
||||
</tb-entity-type-list>
|
||||
<div class="limit-slider-container flex flex-row items-center justify-start xs:flex-col xs:items-stretch">
|
||||
<label translate>notification.threshold</label>
|
||||
<div class="limit-slider-container mb-4 flex flex-row items-center justify-start xs:flex-col xs:items-stretch">
|
||||
<span translate>notification.threshold</span>
|
||||
<div class="flex flex-1 flex-row items-center justify-start">
|
||||
<mat-slider class="flex-1" min="0" max="1" step="0.01" discrete [displayWith]="formatLabel">
|
||||
<mat-slider class="flex-1" min="0" max="100" step="1" discrete [displayWith]="formatLabel">
|
||||
<input matSliderThumb formControlName="threshold">
|
||||
</mat-slider>
|
||||
<mat-form-field class="limit-slider-value">
|
||||
<input matInput formControlName="threshold" type="number" step="0.01"
|
||||
<mat-form-field class="limit-slider-value" subscriptSizing="dynamic">
|
||||
<input matInput formControlName="threshold" type="number" step="1"
|
||||
[value]="entitiesLimitTemplateForm.get('triggerConfig.threshold').value"
|
||||
min="0"
|
||||
max="1"/>
|
||||
max="100"/>
|
||||
<span class="mr-2" matSuffix>%</span>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
@ -591,6 +592,69 @@
|
||||
</section>
|
||||
</form>
|
||||
</mat-step>
|
||||
|
||||
<mat-step *ngIf="ruleNotificationForm.get('triggerType').value === triggerType.RESOURCES_SHORTAGE"
|
||||
[stepControl]="resourceUsageShortageTemplateForm">
|
||||
<ng-template matStepLabel>{{ 'notification.resources-shortage-trigger-settings' | translate }}</ng-template>
|
||||
<form [formGroup]="resourceUsageShortageTemplateForm">
|
||||
<fieldset class="fields-group tb-margin" formGroupName="triggerConfig">
|
||||
<legend translate>notification.filter</legend>
|
||||
<div class="limit-slider-container mb-4 flex flex-row items-center justify-start xs:flex-col xs:items-stretch">
|
||||
<span translate>notification.cpu-threshold</span>
|
||||
<div class="flex flex-1 flex-row items-center justify-start">
|
||||
<mat-slider class="flex-1" min="0" max="100" step="1" discrete [displayWith]="formatLabel">
|
||||
<input matSliderThumb formControlName="cpuThreshold">
|
||||
</mat-slider>
|
||||
<mat-form-field class="limit-slider-value" subscriptSizing="dynamic">
|
||||
<input matInput formControlName="cpuThreshold" type="number" step="1"
|
||||
[value]="resourceUsageShortageTemplateForm.get('triggerConfig.cpuThreshold').value"
|
||||
min="0"
|
||||
max="100"/>
|
||||
<span class="mr-2" matSuffix>%</span>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="limit-slider-container mb-4 flex flex-row items-center justify-start xs:flex-col xs:items-stretch">
|
||||
<span translate>notification.ram-threshold</span>
|
||||
<div class="flex flex-1 flex-row items-center justify-start">
|
||||
<mat-slider class="flex-1" min="0" max="100" step="1" discrete [displayWith]="formatLabel">
|
||||
<input matSliderThumb formControlName="ramThreshold">
|
||||
</mat-slider>
|
||||
<mat-form-field class="limit-slider-value" subscriptSizing="dynamic">
|
||||
<input matInput formControlName="ramThreshold" type="number" step="1"
|
||||
[value]="resourceUsageShortageTemplateForm.get('triggerConfig.ramThreshold').value"
|
||||
min="0"
|
||||
max="100"/>
|
||||
<span class="mr-2" matSuffix>%</span>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="limit-slider-container mb-4 flex flex-row items-center justify-start xs:flex-col xs:items-stretch">
|
||||
<span translate>notification.storage-threshold</span>
|
||||
<div class="flex flex-1 flex-row items-center justify-start">
|
||||
<mat-slider class="flex-1" min="0" max="100" step="1" discrete [displayWith]="formatLabel">
|
||||
<input matSliderThumb formControlName="storageThreshold">
|
||||
</mat-slider>
|
||||
<mat-form-field class="limit-slider-value" subscriptSizing="dynamic">
|
||||
<input matInput formControlName="storageThreshold" type="number" step="1"
|
||||
[value]="resourceUsageShortageTemplateForm.get('triggerConfig.storageThreshold').value"
|
||||
min="0"
|
||||
max="100"/>
|
||||
<span class="mr-2" matSuffix>%</span>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<form [formGroup]="ruleNotificationForm">
|
||||
<section formGroupName="additionalConfig">
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>notification.description</mat-label>
|
||||
<input matInput formControlName="description">
|
||||
</mat-form-field>
|
||||
</section>
|
||||
</form>
|
||||
</mat-step>
|
||||
</mat-horizontal-stepper>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
@ -81,7 +81,7 @@
|
||||
min-width: 364px;
|
||||
}
|
||||
.limit-slider-container {
|
||||
> label {
|
||||
> span {
|
||||
margin-right: 16px;
|
||||
width: min-content;
|
||||
max-width: 40%;
|
||||
|
||||
@ -102,6 +102,7 @@ export class RuleNotificationDialogComponent extends
|
||||
edgeCommunicationFailureTemplateForm: FormGroup;
|
||||
edgeConnectionTemplateForm: FormGroup;
|
||||
taskProcessingFailureTemplateForm: FormGroup;
|
||||
resourceUsageShortageTemplateForm: FormGroup;
|
||||
|
||||
triggerType = TriggerType;
|
||||
triggerTypes: TriggerType[];
|
||||
@ -315,7 +316,7 @@ export class RuleNotificationDialogComponent extends
|
||||
this.entitiesLimitTemplateForm = this.fb.group({
|
||||
triggerConfig: this.fb.group({
|
||||
entityTypes: [],
|
||||
threshold: [.8, [Validators.min(0), Validators.max(1)]]
|
||||
threshold: [80, [Validators.min(0), Validators.max(100)]]
|
||||
})
|
||||
});
|
||||
|
||||
@ -344,6 +345,14 @@ export class RuleNotificationDialogComponent extends
|
||||
})
|
||||
});
|
||||
|
||||
this.resourceUsageShortageTemplateForm = this.fb.group({
|
||||
triggerConfig: this.fb.group({
|
||||
cpuThreshold: [80, [Validators.min(0), Validators.max(100)]],
|
||||
ramThreshold: [80, [Validators.min(0), Validators.max(100)]],
|
||||
storageThreshold: [80, [Validators.min(0), Validators.max(100)]]
|
||||
})
|
||||
});
|
||||
|
||||
this.triggerTypeFormsMap = new Map<TriggerType, FormGroup>([
|
||||
[TriggerType.ALARM, this.alarmTemplateForm],
|
||||
[TriggerType.ALARM_COMMENT, this.alarmCommentTemplateForm],
|
||||
@ -357,7 +366,8 @@ export class RuleNotificationDialogComponent extends
|
||||
[TriggerType.RATE_LIMITS, this.rateLimitsTemplateForm],
|
||||
[TriggerType.EDGE_COMMUNICATION_FAILURE, this.edgeCommunicationFailureTemplateForm],
|
||||
[TriggerType.EDGE_CONNECTION, this.edgeConnectionTemplateForm],
|
||||
[TriggerType.TASK_PROCESSING_FAILURE, this.taskProcessingFailureTemplateForm]
|
||||
[TriggerType.TASK_PROCESSING_FAILURE, this.taskProcessingFailureTemplateForm],
|
||||
[TriggerType.RESOURCES_SHORTAGE, this.resourceUsageShortageTemplateForm]
|
||||
]);
|
||||
|
||||
if (data.isAdd || data.isCopy) {
|
||||
@ -380,6 +390,14 @@ export class RuleNotificationDialogComponent extends
|
||||
this.deviceInactivityTemplateForm.get('triggerConfig.filterByDevice')
|
||||
.patchValue(!!this.ruleNotification.triggerConfig.devices, {onlySelf: true});
|
||||
}
|
||||
if (this.ruleNotification.triggerType === TriggerType.ENTITIES_LIMIT) {
|
||||
this.entitiesLimitTemplateForm.get('triggerConfig.threshold').patchValue(this.ruleNotification.triggerConfig.threshold * 100, {emitEvent: false});
|
||||
}
|
||||
if (this.ruleNotification.triggerType === TriggerType.RESOURCES_SHORTAGE) {
|
||||
this.resourceUsageShortageTemplateForm.get('triggerConfig.cpuThreshold').patchValue(this.ruleNotification.triggerConfig.cpuThreshold * 100, {emitEvent: false});
|
||||
this.resourceUsageShortageTemplateForm.get('triggerConfig.ramThreshold').patchValue(this.ruleNotification.triggerConfig.ramThreshold * 100, {emitEvent: false});
|
||||
this.resourceUsageShortageTemplateForm.get('triggerConfig.storageThreshold').patchValue(this.ruleNotification.triggerConfig.storageThreshold * 100, {emitEvent: false});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -428,6 +446,14 @@ export class RuleNotificationDialogComponent extends
|
||||
if (triggerType === TriggerType.DEVICE_ACTIVITY) {
|
||||
delete formValue.triggerConfig.filterByDevice;
|
||||
}
|
||||
if (triggerType === TriggerType.ENTITIES_LIMIT) {
|
||||
formValue.triggerConfig.threshold = formValue.triggerConfig.threshold / 100;
|
||||
}
|
||||
if (triggerType === TriggerType.RESOURCES_SHORTAGE) {
|
||||
formValue.triggerConfig.cpuThreshold = formValue.triggerConfig.cpuThreshold / 100;
|
||||
formValue.triggerConfig.ramThreshold = formValue.triggerConfig.ramThreshold / 100;
|
||||
formValue.triggerConfig.storageThreshold = formValue.triggerConfig.storageThreshold / 100;
|
||||
}
|
||||
formValue.recipientsConfig.triggerType = triggerType;
|
||||
formValue.triggerConfig.triggerType = triggerType;
|
||||
if (this.ruleNotification && !this.data.isCopy) {
|
||||
@ -483,8 +509,7 @@ export class RuleNotificationDialogComponent extends
|
||||
}
|
||||
|
||||
formatLabel(value: number): string {
|
||||
const formatValue = (value * 100).toFixed();
|
||||
return `${formatValue}%`;
|
||||
return `${value}%`;
|
||||
}
|
||||
|
||||
private isSysAdmin(): boolean {
|
||||
@ -497,7 +522,8 @@ export class RuleNotificationDialogComponent extends
|
||||
TriggerType.API_USAGE_LIMIT,
|
||||
TriggerType.NEW_PLATFORM_VERSION,
|
||||
TriggerType.RATE_LIMITS,
|
||||
TriggerType.TASK_PROCESSING_FAILURE
|
||||
TriggerType.TASK_PROCESSING_FAILURE,
|
||||
TriggerType.RESOURCES_SHORTAGE
|
||||
]);
|
||||
|
||||
if (this.isSysAdmin()) {
|
||||
|
||||
@ -180,7 +180,8 @@ export class TemplateNotificationDialogComponent
|
||||
NotificationType.API_USAGE_LIMIT,
|
||||
NotificationType.NEW_PLATFORM_VERSION,
|
||||
NotificationType.RATE_LIMITS,
|
||||
NotificationType.TASK_PROCESSING_FAILURE
|
||||
NotificationType.TASK_PROCESSING_FAILURE,
|
||||
NotificationType.RESOURCES_SHORTAGE
|
||||
]);
|
||||
|
||||
if (this.isSysAdmin()) {
|
||||
|
||||
@ -129,7 +129,7 @@ export interface NotificationRule extends Omit<BaseData<NotificationRuleId>, 'la
|
||||
export type NotificationRuleTriggerConfig = Partial<AlarmNotificationRuleTriggerConfig & DeviceInactivityNotificationRuleTriggerConfig &
|
||||
EntityActionNotificationRuleTriggerConfig & AlarmCommentNotificationRuleTriggerConfig & AlarmAssignmentNotificationRuleTriggerConfig &
|
||||
RuleEngineLifecycleEventNotificationRuleTriggerConfig & EntitiesLimitNotificationRuleTriggerConfig &
|
||||
ApiUsageLimitNotificationRuleTriggerConfig & RateLimitsNotificationRuleTriggerConfig>;
|
||||
ApiUsageLimitNotificationRuleTriggerConfig & RateLimitsNotificationRuleTriggerConfig & ResourceUsageShortageNotificationRuleTriggerConfig>;
|
||||
|
||||
export interface AlarmNotificationRuleTriggerConfig {
|
||||
alarmTypes?: Array<string>;
|
||||
@ -183,6 +183,12 @@ export interface EntitiesLimitNotificationRuleTriggerConfig {
|
||||
threshold: number;
|
||||
}
|
||||
|
||||
export interface ResourceUsageShortageNotificationRuleTriggerConfig {
|
||||
cpuThreshold: number;
|
||||
ramThreshold: number;
|
||||
storageThreshold: number;
|
||||
}
|
||||
|
||||
export interface ApiUsageLimitNotificationRuleTriggerConfig {
|
||||
apiFeatures: ApiFeature[];
|
||||
notifyOn: ApiUsageStateValue[];
|
||||
@ -526,7 +532,8 @@ export enum NotificationType {
|
||||
RATE_LIMITS = 'RATE_LIMITS',
|
||||
EDGE_CONNECTION = 'EDGE_CONNECTION',
|
||||
EDGE_COMMUNICATION_FAILURE = 'EDGE_COMMUNICATION_FAILURE',
|
||||
TASK_PROCESSING_FAILURE = 'TASK_PROCESSING_FAILURE'
|
||||
TASK_PROCESSING_FAILURE = 'TASK_PROCESSING_FAILURE',
|
||||
RESOURCES_SHORTAGE = 'RESOURCES_SHORTAGE'
|
||||
}
|
||||
|
||||
export const NotificationTypeIcons = new Map<NotificationType, string | null>([
|
||||
@ -538,7 +545,8 @@ export const NotificationTypeIcons = new Map<NotificationType, string | null>([
|
||||
[NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'settings_ethernet'],
|
||||
[NotificationType.ENTITIES_LIMIT, 'data_thresholding'],
|
||||
[NotificationType.API_USAGE_LIMIT, 'insert_chart'],
|
||||
[NotificationType.TASK_PROCESSING_FAILURE, 'warning']
|
||||
[NotificationType.TASK_PROCESSING_FAILURE, 'warning'],
|
||||
[NotificationType.RESOURCES_SHORTAGE, 'warning']
|
||||
]);
|
||||
|
||||
export const AlarmSeverityNotificationColors = new Map<AlarmSeverity, string>(
|
||||
@ -657,6 +665,12 @@ export const NotificationTemplateTypeTranslateMap = new Map<NotificationType, No
|
||||
helpId: 'notification/task_processing_failure'
|
||||
}
|
||||
],
|
||||
[NotificationType.RESOURCES_SHORTAGE,
|
||||
{
|
||||
name: 'notification.template-type.resources-shortage',
|
||||
helpId: 'notification/resources_shortage'
|
||||
}
|
||||
]
|
||||
]);
|
||||
|
||||
export enum TriggerType {
|
||||
@ -673,6 +687,7 @@ export enum TriggerType {
|
||||
EDGE_CONNECTION = 'EDGE_CONNECTION',
|
||||
EDGE_COMMUNICATION_FAILURE = 'EDGE_COMMUNICATION_FAILURE',
|
||||
TASK_PROCESSING_FAILURE = 'TASK_PROCESSING_FAILURE',
|
||||
RESOURCES_SHORTAGE = 'RESOURCES_SHORTAGE'
|
||||
}
|
||||
|
||||
export const TriggerTypeTranslationMap = new Map<TriggerType, string>([
|
||||
@ -688,7 +703,8 @@ export const TriggerTypeTranslationMap = new Map<TriggerType, string>([
|
||||
[TriggerType.RATE_LIMITS, 'notification.trigger.rate-limits'],
|
||||
[TriggerType.EDGE_CONNECTION, 'notification.trigger.edge-connection'],
|
||||
[TriggerType.EDGE_COMMUNICATION_FAILURE, 'notification.trigger.edge-communication-failure'],
|
||||
[TriggerType.TASK_PROCESSING_FAILURE, 'notification.trigger.task-processing-failure']
|
||||
[TriggerType.TASK_PROCESSING_FAILURE, 'notification.trigger.task-processing-failure'],
|
||||
[TriggerType.RESOURCES_SHORTAGE, 'notification.trigger.resources-shortage']
|
||||
]);
|
||||
|
||||
export interface NotificationUserSettings {
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
#### Resources shortage notification templatization
|
||||
|
||||
<div class="divider"></div>
|
||||
<br/>
|
||||
|
||||
Notification subject and message fields support templatization.
|
||||
The list of available templatization parameters depends on the template type.
|
||||
See the available types and parameters below:
|
||||
|
||||
Available template parameters:
|
||||
|
||||
* `resource` - the resource name;
|
||||
* `usage` - the resource usage value;
|
||||
|
||||
Parameter names must be wrapped using `${...}`. For example: `${resource}`.
|
||||
You may also modify the value of the parameter with one of the suffixes:
|
||||
|
||||
* `upperCase`, for example - `${resource:upperCase}`
|
||||
* `lowerCase`, for example - `${resource:lowerCase}`
|
||||
* `capitalize`, for example - `${resource:capitalize}`
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
##### Examples
|
||||
|
||||
Let's assume there is a resource usage shortage and the system is low on free resources (CPU, RAM, or Storage).
|
||||
The following template:
|
||||
|
||||
```text
|
||||
Warning: ${resource} is critically high at ${usage}%
|
||||
{:copy-code}
|
||||
```
|
||||
|
||||
will be transformed to:
|
||||
|
||||
```text
|
||||
Warning: CPU is critically high at 83%
|
||||
```
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@ -3876,6 +3876,7 @@
|
||||
"new-platform-version-trigger-settings": "New platform version trigger settings",
|
||||
"rate-limits-trigger-settings": "Exceeded rate limits trigger settings",
|
||||
"task-processing-failure-trigger-settings": "Task processing failure trigger settings",
|
||||
"resources-shortage-trigger-settings": "Resources shortage trigger settings",
|
||||
"at-least-one-should-be-selected": "At least one should be selected",
|
||||
"basic-settings": "Basic settings",
|
||||
"button-text": "Button text",
|
||||
@ -3890,6 +3891,7 @@
|
||||
"create-new": "Create new",
|
||||
"created": "Created",
|
||||
"customize-messages": "Customize messages",
|
||||
"cpu-threshold": "CPU threshold",
|
||||
"delete-notification-text": "Be careful, after the confirmation the notification will become unrecoverable.",
|
||||
"delete-notification-title": "Are you sure you want to delete the notification?",
|
||||
"delete-notifications-text": "Be careful, after the confirmation notifications will become unrecoverable.",
|
||||
@ -4005,6 +4007,7 @@
|
||||
"only-rule-chain-lifecycle-failures": "Only rule chain lifecycle failures",
|
||||
"only-rule-node-lifecycle-failures": "Only rule node lifecycle failures",
|
||||
"platform-users": "Platform users",
|
||||
"ram-threshold": "RAM threshold",
|
||||
"rate-limits": "Rate limits",
|
||||
"rate-limits-hint": "If the field is empty, the trigger will be applied to all rate limits",
|
||||
"recipient": "Recipient",
|
||||
@ -4070,6 +4073,7 @@
|
||||
"start-from-scratch": "Start from scratch",
|
||||
"status": "Status",
|
||||
"stop-escalation-alarm-status-become": "Stop the escalation on the alarm status become:",
|
||||
"storage-threshold": "Storage threshold",
|
||||
"subject": "Subject",
|
||||
"subject-required": "Subject is required",
|
||||
"subject-max-length": "Subject should be less than or equal to {{ length }} characters",
|
||||
@ -4091,7 +4095,8 @@
|
||||
"rate-limits": "Exceeded rate limits",
|
||||
"edge-communication-failure": "Edge communication failure",
|
||||
"edge-connection": "Edge connection",
|
||||
"task-processing-failure": "Task processing failure"
|
||||
"task-processing-failure": "Task processing failure",
|
||||
"resources-shortage": "Resources shortage"
|
||||
},
|
||||
"templates": "Templates",
|
||||
"notification-templates": "Notifications / Templates",
|
||||
@ -4115,6 +4120,7 @@
|
||||
"edge-connection": "Edge connection",
|
||||
"edge-communication-failure": "Edge communication failure",
|
||||
"task-processing-failure": "Task processing failure",
|
||||
"resources-shortage": "Resources shortage",
|
||||
"trigger": "Trigger",
|
||||
"trigger-required": "Trigger is required"
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user