WIP - Alarmed metrics API changes
- Changed alarms resource to alarm-definitions - Added new alarms resource that tracks alarm/metric associations Change-Id: Ib660eabd2c34001d165bf22bc9941b6a1cb4fcef
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,6 @@
|
||||
*/
|
||||
package com.hpcloud.mon;
|
||||
|
||||
import com.hpcloud.mon.infrastructure.servlet.RoleAuthorizationFilter;
|
||||
import io.dropwizard.Application;
|
||||
import io.dropwizard.jdbi.bundles.DBIExceptionsBundle;
|
||||
import io.dropwizard.setup.Bootstrap;
|
||||
@@ -39,6 +38,8 @@ import com.hpcloud.mon.bundle.SwaggerBundle;
|
||||
import com.hpcloud.mon.infrastructure.servlet.MockAuthenticationFilter;
|
||||
import com.hpcloud.mon.infrastructure.servlet.PostAuthenticationFilter;
|
||||
import com.hpcloud.mon.infrastructure.servlet.PreAuthenticationFilter;
|
||||
import com.hpcloud.mon.infrastructure.servlet.RoleAuthorizationFilter;
|
||||
import com.hpcloud.mon.resource.AlarmDefinitionResource;
|
||||
import com.hpcloud.mon.resource.AlarmResource;
|
||||
import com.hpcloud.mon.resource.MeasurementResource;
|
||||
import com.hpcloud.mon.resource.MetricResource;
|
||||
@@ -77,12 +78,14 @@ public class MonApiApplication extends Application<MonApiConfiguration> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run(MonApiConfiguration config, Environment environment) throws Exception {
|
||||
/** Wire services */
|
||||
Injector.registerModules(new MonApiModule(environment, config));
|
||||
|
||||
/** Configure resources */
|
||||
environment.jersey().register(Injector.getInstance(VersionResource.class));
|
||||
environment.jersey().register(Injector.getInstance(AlarmDefinitionResource.class));
|
||||
environment.jersey().register(Injector.getInstance(AlarmResource.class));
|
||||
environment.jersey().register(Injector.getInstance(MetricResource.class));
|
||||
environment.jersey().register(Injector.getInstance(MeasurementResource.class));
|
||||
@@ -153,15 +156,18 @@ public class MonApiApplication extends Application<MonApiConfiguration> {
|
||||
tokenAuthFilter.setInitParameters(authInitParams);
|
||||
|
||||
Dynamic postAuthenticationFilter =
|
||||
environment.servlets().addFilter("post-auth",
|
||||
new PostAuthenticationFilter(config.middleware.defaultAuthorizedRoles, config.middleware.agentAuthorizedRoles));
|
||||
environment.servlets().addFilter(
|
||||
"post-auth",
|
||||
new PostAuthenticationFilter(config.middleware.defaultAuthorizedRoles,
|
||||
config.middleware.agentAuthorizedRoles));
|
||||
postAuthenticationFilter.addMappingForUrlPatterns(null, true, "/");
|
||||
postAuthenticationFilter.addMappingForUrlPatterns(null, true, "/v2.0/*");
|
||||
|
||||
environment.jersey().getResourceConfig().getContainerRequestFilters().add(new RoleAuthorizationFilter());
|
||||
environment.jersey().getResourceConfig().getContainerRequestFilters()
|
||||
.add(new RoleAuthorizationFilter());
|
||||
} else {
|
||||
Dynamic mockAuthenticationFilter =
|
||||
environment.servlets().addFilter("mock-auth", new MockAuthenticationFilter());
|
||||
Dynamic mockAuthenticationFilter =
|
||||
environment.servlets().addFilter("mock-auth", new MockAuthenticationFilter());
|
||||
mockAuthenticationFilter.addMappingForUrlPatterns(null, true, "/");
|
||||
mockAuthenticationFilter.addMappingForUrlPatterns(null, true, "/v2.0/*");
|
||||
}
|
||||
|
||||
315
src/main/java/com/hpcloud/mon/app/AlarmDefinitionService.java
Normal file
315
src/main/java/com/hpcloud/mon/app/AlarmDefinitionService.java
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 com.hpcloud.mon.app;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import kafka.javaapi.producer.Producer;
|
||||
import kafka.producer.KeyedMessage;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.hpcloud.mon.MonApiConfiguration;
|
||||
import com.hpcloud.mon.app.command.UpdateAlarmDefinitionCommand;
|
||||
import com.hpcloud.mon.common.event.AlarmDefinitionCreatedEvent;
|
||||
import com.hpcloud.mon.common.event.AlarmDefinitionDeletedEvent;
|
||||
import com.hpcloud.mon.common.event.AlarmDefinitionUpdatedEvent;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmExpression;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmSubExpression;
|
||||
import com.hpcloud.mon.common.model.metric.MetricDefinition;
|
||||
import com.hpcloud.mon.domain.exception.EntityExistsException;
|
||||
import com.hpcloud.mon.domain.exception.EntityNotFoundException;
|
||||
import com.hpcloud.mon.domain.exception.InvalidEntityException;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinition;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinitionRepository;
|
||||
import com.hpcloud.mon.domain.model.notificationmethod.NotificationMethodRepository;
|
||||
import com.hpcloud.util.Exceptions;
|
||||
import com.hpcloud.util.Serialization;
|
||||
|
||||
/**
|
||||
* Services alarm definition related requests.
|
||||
*/
|
||||
public class AlarmDefinitionService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AlarmService.class);
|
||||
|
||||
private final MonApiConfiguration config;
|
||||
private final Producer<String, String> producer;
|
||||
private final AlarmDefinitionRepository repo;
|
||||
private final NotificationMethodRepository notificationMethodRepo;
|
||||
|
||||
@Inject
|
||||
public AlarmDefinitionService(MonApiConfiguration config, Producer<String, String> producer,
|
||||
AlarmDefinitionRepository repo, NotificationMethodRepository notificationMethodRepo) {
|
||||
this.config = config;
|
||||
this.producer = producer;
|
||||
this.repo = repo;
|
||||
this.notificationMethodRepo = notificationMethodRepo;
|
||||
}
|
||||
|
||||
static class SubExpressions {
|
||||
/** Sub expressions which have been removed from an updated alarm expression. */
|
||||
Map<String, AlarmSubExpression> oldAlarmSubExpressions;
|
||||
/** Sub expressions which have had their operator or threshold changed. */
|
||||
Map<String, AlarmSubExpression> changedSubExpressions;
|
||||
/** Sub expressions which have not changed. */
|
||||
Map<String, AlarmSubExpression> unchangedSubExpressions;
|
||||
/** Sub expressions which have been added to an updated alarm expression. */
|
||||
Map<String, AlarmSubExpression> newAlarmSubExpressions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an alarm definition and publishes an AlarmDefinitionCreatedEvent. Note, the event is
|
||||
* published first since chances of failure are higher.
|
||||
*
|
||||
* @throws EntityExistsException if an alarm already exists for the name
|
||||
* @throws InvalidEntityException if one of the actions cannot be found
|
||||
*/
|
||||
public AlarmDefinition create(String tenantId, String name, @Nullable String description,
|
||||
String severity, String expression, AlarmExpression alarmExpression, List<String> matchBy,
|
||||
List<String> alarmActions, @Nullable List<String> okActions,
|
||||
@Nullable List<String> undeterminedActions) {
|
||||
// Assert no alarm exists by the name
|
||||
if (repo.exists(tenantId, name))
|
||||
throw new EntityExistsException(
|
||||
"An alarm definition already exists for project / tenant: %s named: %s", tenantId, name);
|
||||
|
||||
assertActionsExist(tenantId, alarmActions, okActions, undeterminedActions);
|
||||
|
||||
Map<String, AlarmSubExpression> subAlarms = new HashMap<String, AlarmSubExpression>();
|
||||
for (AlarmSubExpression subExpression : alarmExpression.getSubExpressions())
|
||||
subAlarms.put(UUID.randomUUID().toString(), subExpression);
|
||||
|
||||
String alarmDefId = UUID.randomUUID().toString();
|
||||
AlarmDefinition alarm = null;
|
||||
|
||||
try {
|
||||
LOG.debug("Creating alarm definition {} for tenant {}", name, tenantId);
|
||||
alarm =
|
||||
repo.create(tenantId, alarmDefId, name, description, severity, expression, subAlarms,
|
||||
matchBy, alarmActions, okActions, undeterminedActions);
|
||||
|
||||
// Notify interested parties of new alarm
|
||||
String event =
|
||||
Serialization.toJson(new AlarmDefinitionCreatedEvent(tenantId, alarmDefId, name,
|
||||
description, expression, subAlarms, matchBy));
|
||||
producer.send(new KeyedMessage<>(config.eventsTopic, tenantId, event));
|
||||
|
||||
return alarm;
|
||||
} catch (Exception e) {
|
||||
if (alarm != null)
|
||||
try {
|
||||
repo.deleteById(tenantId, alarm.getId());
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
throw Exceptions.uncheck(e, "Error creating alarm definition for project / tenant %s",
|
||||
tenantId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the alarm definition identified by the {@code alarmDefId}.
|
||||
*
|
||||
* @throws EntityNotFoundException if the alarm cannot be found
|
||||
*/
|
||||
public void delete(String tenantId, String alarmDefId) {
|
||||
Map<String, MetricDefinition> subAlarmMetricDefs =
|
||||
repo.findSubAlarmMetricDefinitions(alarmDefId);
|
||||
repo.deleteById(tenantId, alarmDefId);
|
||||
|
||||
// Notify interested parties of alarm definition deletion
|
||||
String event =
|
||||
Serialization.toJson(new AlarmDefinitionDeletedEvent(alarmDefId, subAlarmMetricDefs));
|
||||
producer.send(new KeyedMessage<>(config.eventsTopic, tenantId, event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the alarm definition for the {@code tenantId} and {@code alarmDefId} to the state of
|
||||
* the {@code command}.
|
||||
*
|
||||
* @throws EntityNotFoundException if the alarm cannot be found
|
||||
* @throws InvalidEntityException if one of the actions cannot be found
|
||||
*/
|
||||
public AlarmDefinition update(String tenantId, String alarmDefId,
|
||||
AlarmExpression alarmExpression, UpdateAlarmDefinitionCommand command) {
|
||||
assertAlarmDefinitionExists(tenantId, alarmDefId, command.alarmActions, command.okActions,
|
||||
command.undeterminedActions);
|
||||
updateInternal(tenantId, alarmDefId, false, command.name, command.description,
|
||||
command.expression, command.matchBy, command.severity, alarmExpression,
|
||||
command.actionsEnabled, command.alarmActions, command.okActions,
|
||||
command.undeterminedActions);
|
||||
return new AlarmDefinition(alarmDefId, command.name, command.description, command.severity,
|
||||
command.expression, command.matchBy, command.actionsEnabled, command.alarmActions,
|
||||
command.okActions, command.undeterminedActions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches the alarm definition for the {@code tenantId} and {@code alarmDefId} to the state of
|
||||
* the {@code fields}.
|
||||
*
|
||||
* @throws EntityNotFoundException if the alarm cannot be found
|
||||
* @throws InvalidEntityException if one of the actions cannot be found
|
||||
*/
|
||||
public AlarmDefinition patch(String tenantId, String alarmDefId, String name, String description,
|
||||
String severity, String expression, AlarmExpression alarmExpression, List<String> matchBy,
|
||||
AlarmState state, Boolean enabled, List<String> alarmActions, List<String> okActions,
|
||||
List<String> undeterminedActions) {
|
||||
AlarmDefinition alarm =
|
||||
assertAlarmDefinitionExists(tenantId, alarmDefId, alarmActions, okActions,
|
||||
undeterminedActions);
|
||||
name = name == null ? alarm.getName() : name;
|
||||
description = description == null ? alarm.getDescription() : description;
|
||||
expression = expression == null ? alarm.getExpression() : expression;
|
||||
severity = severity == null ? alarm.getSeverity() : severity;
|
||||
alarmExpression = alarmExpression == null ? AlarmExpression.of(expression) : alarmExpression;
|
||||
enabled = enabled == null ? alarm.isActionsEnabled() : enabled;
|
||||
|
||||
updateInternal(tenantId, alarmDefId, true, name, description, expression, matchBy, severity,
|
||||
alarmExpression, enabled, alarmActions, okActions, undeterminedActions);
|
||||
|
||||
return new AlarmDefinition(alarmDefId, name, description, severity, expression, matchBy,
|
||||
enabled, alarmActions == null ? alarm.getAlarmActions() : alarmActions,
|
||||
okActions == null ? alarm.getOkActions() : okActions,
|
||||
undeterminedActions == null ? alarm.getUndeterminedActions() : undeterminedActions);
|
||||
}
|
||||
|
||||
private void updateInternal(String tenantId, String alarmDefId, boolean patch, String name,
|
||||
String description, String expression, List<String> matchBy, String severity,
|
||||
AlarmExpression alarmExpression, Boolean enabled, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions) {
|
||||
SubExpressions subExpressions = subExpressionsFor(alarmDefId, alarmExpression);
|
||||
|
||||
try {
|
||||
LOG.debug("Updating alarm definition {} for tenant {}", name, tenantId);
|
||||
repo.update(tenantId, alarmDefId, patch, name, description, expression, matchBy, severity,
|
||||
enabled, subExpressions.oldAlarmSubExpressions.keySet(),
|
||||
subExpressions.changedSubExpressions, subExpressions.newAlarmSubExpressions,
|
||||
alarmActions, okActions, undeterminedActions);
|
||||
|
||||
// Notify interested parties of updated alarm
|
||||
String event =
|
||||
Serialization.toJson(new AlarmDefinitionUpdatedEvent(tenantId, alarmDefId, name,
|
||||
description, expression, matchBy, enabled, subExpressions.oldAlarmSubExpressions,
|
||||
subExpressions.changedSubExpressions, subExpressions.unchangedSubExpressions,
|
||||
subExpressions.newAlarmSubExpressions));
|
||||
producer.send(new KeyedMessage<>(config.eventsTopic, tenantId, event));
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.uncheck(e, "Error updating alarm definition for project / tenant %s",
|
||||
tenantId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an entry containing Maps of old, changed, and new sub expressions by comparing the
|
||||
* {@code alarmExpression} to the existing sub expressions for the {@code alarmDefId}.
|
||||
*/
|
||||
SubExpressions subExpressionsFor(String alarmDefId, AlarmExpression alarmExpression) {
|
||||
BiMap<String, AlarmSubExpression> oldExpressions =
|
||||
HashBiMap.create(repo.findSubExpressions(alarmDefId));
|
||||
Set<AlarmSubExpression> oldSet = oldExpressions.inverse().keySet();
|
||||
Set<AlarmSubExpression> newSet = new HashSet<>(alarmExpression.getSubExpressions());
|
||||
|
||||
// Identify old or changed expressions
|
||||
Set<AlarmSubExpression> oldOrChangedExpressions =
|
||||
new HashSet<>(Sets.difference(oldSet, newSet));
|
||||
|
||||
// Identify new or changed expressions
|
||||
Set<AlarmSubExpression> newOrChangedExpressions =
|
||||
new HashSet<>(Sets.difference(newSet, oldSet));
|
||||
|
||||
// Find changed expressions
|
||||
Map<String, AlarmSubExpression> changedExpressions = new HashMap<>();
|
||||
for (Iterator<AlarmSubExpression> oldIt = oldOrChangedExpressions.iterator(); oldIt.hasNext();) {
|
||||
AlarmSubExpression oldExpr = oldIt.next();
|
||||
for (Iterator<AlarmSubExpression> newIt = newOrChangedExpressions.iterator(); newIt.hasNext();) {
|
||||
AlarmSubExpression newExpr = newIt.next();
|
||||
if (sameKeyFields(oldExpr, newExpr)) {
|
||||
oldIt.remove();
|
||||
newIt.remove();
|
||||
changedExpressions.put(oldExpressions.inverse().get(oldExpr), newExpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the list of unchanged expressions
|
||||
BiMap<String, AlarmSubExpression> unchangedExpressions = HashBiMap.create(oldExpressions);
|
||||
unchangedExpressions.values().removeAll(oldOrChangedExpressions);
|
||||
unchangedExpressions.keySet().removeAll(changedExpressions.keySet());
|
||||
|
||||
// Remove old sub expressions
|
||||
oldExpressions.values().retainAll(oldOrChangedExpressions);
|
||||
|
||||
// Create IDs for new expressions
|
||||
Map<String, AlarmSubExpression> newExpressions = new HashMap<>();
|
||||
for (AlarmSubExpression expression : newOrChangedExpressions)
|
||||
newExpressions.put(UUID.randomUUID().toString(), expression);
|
||||
|
||||
SubExpressions subExpressions = new SubExpressions();
|
||||
subExpressions.oldAlarmSubExpressions = oldExpressions;
|
||||
subExpressions.changedSubExpressions = changedExpressions;
|
||||
subExpressions.unchangedSubExpressions = unchangedExpressions;
|
||||
subExpressions.newAlarmSubExpressions = newExpressions;
|
||||
return subExpressions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether all of the fields of {@code a} and {@code b} are the same except the operator
|
||||
* and threshold.
|
||||
*/
|
||||
private boolean sameKeyFields(AlarmSubExpression a, AlarmSubExpression b) {
|
||||
return a.getMetricDefinition().equals(b.getMetricDefinition())
|
||||
&& a.getFunction().equals(b.getFunction()) && a.getPeriod() == b.getPeriod()
|
||||
&& a.getPeriods() == b.getPeriods();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts an alarm definition exists for the {@code alarmDefId} as well as the actions.
|
||||
*
|
||||
* @throws EntityNotFoundException if the alarm cannot be found
|
||||
*/
|
||||
private AlarmDefinition assertAlarmDefinitionExists(String tenantId, String alarmDefId,
|
||||
List<String> alarmActions, List<String> okActions, List<String> undeterminedActions) {
|
||||
AlarmDefinition alarm = repo.findById(tenantId, alarmDefId);
|
||||
assertActionsExist(tenantId, alarmActions, okActions, undeterminedActions);
|
||||
return alarm;
|
||||
}
|
||||
|
||||
private void assertActionsExist(String tenantId, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions) {
|
||||
Set<String> actions = new HashSet<>();
|
||||
if (alarmActions != null)
|
||||
actions.addAll(alarmActions);
|
||||
if (okActions != null)
|
||||
actions.addAll(okActions);
|
||||
if (undeterminedActions != null)
|
||||
actions.addAll(undeterminedActions);
|
||||
if (!actions.isEmpty())
|
||||
for (String action : actions)
|
||||
if (!notificationMethodRepo.exists(tenantId, action))
|
||||
throw new InvalidEntityException("No notification method exists for action %s", action);
|
||||
}
|
||||
}
|
||||
@@ -13,15 +13,9 @@
|
||||
*/
|
||||
package com.hpcloud.mon.app;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import kafka.javaapi.producer.Producer;
|
||||
@@ -30,30 +24,24 @@ import kafka.producer.KeyedMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.hpcloud.mon.MonApiConfiguration;
|
||||
import com.hpcloud.mon.app.command.UpdateAlarmCommand;
|
||||
import com.hpcloud.mon.common.event.AlarmCreatedEvent;
|
||||
import com.hpcloud.mon.common.event.AlarmDeletedEvent;
|
||||
import com.hpcloud.mon.common.event.AlarmStateTransitionedEvent;
|
||||
import com.hpcloud.mon.common.event.AlarmUpdatedEvent;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmExpression;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmSubExpression;
|
||||
import com.hpcloud.mon.common.model.metric.MetricDefinition;
|
||||
import com.hpcloud.mon.domain.exception.EntityExistsException;
|
||||
import com.hpcloud.mon.domain.exception.EntityNotFoundException;
|
||||
import com.hpcloud.mon.domain.exception.InvalidEntityException;
|
||||
import com.hpcloud.mon.domain.model.alarm.Alarm;
|
||||
import com.hpcloud.mon.domain.model.alarm.AlarmRepository;
|
||||
import com.hpcloud.mon.domain.model.notificationmethod.NotificationMethodRepository;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinition;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinitionRepository;
|
||||
import com.hpcloud.util.Exceptions;
|
||||
import com.hpcloud.util.Serialization;
|
||||
|
||||
/**
|
||||
* Services alarm related requests.
|
||||
* Services alarmed metric related requests.
|
||||
*/
|
||||
public class AlarmService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AlarmService.class);
|
||||
@@ -61,110 +49,37 @@ public class AlarmService {
|
||||
private final MonApiConfiguration config;
|
||||
private final Producer<String, String> producer;
|
||||
private final AlarmRepository repo;
|
||||
private final NotificationMethodRepository notificationMethodRepo;
|
||||
private final AlarmDefinitionRepository alarmDefRepo;
|
||||
|
||||
@Inject
|
||||
public AlarmService(MonApiConfiguration config, Producer<String, String> producer,
|
||||
AlarmRepository repo, NotificationMethodRepository notificationMethodRepo) {
|
||||
AlarmRepository repo, AlarmDefinitionRepository alarmDefRepo) {
|
||||
this.config = config;
|
||||
this.producer = producer;
|
||||
this.repo = repo;
|
||||
this.notificationMethodRepo = notificationMethodRepo;
|
||||
}
|
||||
|
||||
static class SubExpressions {
|
||||
/** Sub expressions which have been removed from an updated alarm expression. */
|
||||
Map<String, AlarmSubExpression> oldAlarmSubExpressions;
|
||||
/** Sub expressions which have had their operator or threshold changed. */
|
||||
Map<String, AlarmSubExpression> changedSubExpressions;
|
||||
/** Sub expressions which have not changed. */
|
||||
Map<String, AlarmSubExpression> unchangedSubExpressions;
|
||||
/** Sub expressions which have been added to an updated alarm expression. */
|
||||
Map<String, AlarmSubExpression> newAlarmSubExpressions;
|
||||
this.alarmDefRepo = alarmDefRepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an alarm and publishes an AlarmCreatedEvent. Note, the event is published first since
|
||||
* chances of failure are higher.
|
||||
*
|
||||
* @throws EntityExistsException if an alarm already exists for the name
|
||||
* @throws InvalidEntityException if one of the actions cannot be found
|
||||
*/
|
||||
public Alarm create(String tenantId, String name, @Nullable String description, String severity,
|
||||
String expression, AlarmExpression alarmExpression, List<String> alarmActions,
|
||||
@Nullable List<String> okActions, @Nullable List<String> undeterminedActions) {
|
||||
// Assert no alarm exists by the name
|
||||
if (repo.exists(tenantId, name))
|
||||
throw new EntityExistsException("An alarm already exists for project / tenant: %s named: %s",
|
||||
tenantId, name);
|
||||
|
||||
assertActionsExist(tenantId, alarmActions, okActions, undeterminedActions);
|
||||
|
||||
Map<String, AlarmSubExpression> subAlarms = new HashMap<String, AlarmSubExpression>();
|
||||
for (AlarmSubExpression subExpression : alarmExpression.getSubExpressions())
|
||||
subAlarms.put(UUID.randomUUID().toString(), subExpression);
|
||||
|
||||
String alarmId = UUID.randomUUID().toString();
|
||||
Alarm alarm = null;
|
||||
|
||||
try {
|
||||
LOG.debug("Creating alarm {} for tenant {}", name, tenantId);
|
||||
alarm =
|
||||
repo.create(tenantId, alarmId, name, description, severity, expression, subAlarms,
|
||||
alarmActions, okActions, undeterminedActions);
|
||||
|
||||
// Notify interested parties of new alarm
|
||||
String event =
|
||||
Serialization
|
||||
.toJson(new AlarmCreatedEvent(tenantId, alarmId, name, expression, subAlarms));
|
||||
producer.send(new KeyedMessage<>(config.eventsTopic, tenantId, event));
|
||||
|
||||
return alarm;
|
||||
} catch (Exception e) {
|
||||
if (alarm != null)
|
||||
try {
|
||||
repo.deleteById(tenantId, alarm.getId());
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
throw Exceptions.uncheck(e, "Error creating alarm for project / tenant %s", tenantId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the alarm identified by the {@code alarmId}.
|
||||
* Deletes the alarm identified by the {@code alarmId
|
||||
* }.
|
||||
*
|
||||
* @throws EntityNotFoundException if the alarm cannot be found
|
||||
*/
|
||||
public void delete(String tenantId, String alarmId) {
|
||||
Map<String, MetricDefinition> subAlarmMetricDefs = repo.findSubAlarmMetricDefinitions(alarmId);
|
||||
repo.deleteById(tenantId, alarmId);
|
||||
Alarm alarm = repo.findById(alarmId);
|
||||
Map<String, MetricDefinition> subAlarmMetricDefs =
|
||||
alarmDefRepo.findSubAlarmMetricDefinitions(alarm.getAlarmDefinitionId());
|
||||
List<MetricDefinition> metrics = repo.findMetrics(alarmId);
|
||||
repo.deleteById(alarmId);
|
||||
|
||||
// Notify interested parties of alarm deletion
|
||||
String event =
|
||||
Serialization.toJson(new AlarmDeletedEvent(tenantId, alarmId, subAlarmMetricDefs));
|
||||
Serialization.toJson(new AlarmDeletedEvent(tenantId, alarmId, metrics, alarm
|
||||
.getAlarmDefinitionId(), subAlarmMetricDefs));
|
||||
producer.send(new KeyedMessage<>(config.eventsTopic, tenantId, event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the alarm for the {@code tenantId} and {@code alarmId} to the state of the
|
||||
* {@code command}.
|
||||
*
|
||||
* @throws EntityNotFoundException if the alarm cannot be found
|
||||
* @throws InvalidEntityException if one of the actions cannot be found
|
||||
*/
|
||||
public Alarm update(String tenantId, String alarmId, AlarmExpression alarmExpression,
|
||||
UpdateAlarmCommand command) {
|
||||
Alarm alarm =
|
||||
assertAlarmExists(tenantId, alarmId, command.alarmActions, command.okActions,
|
||||
command.undeterminedActions);
|
||||
updateInternal(tenantId, alarmId, false, command.name, command.description, command.expression,
|
||||
command.severity, alarmExpression, alarm.getState(), command.state, command.actionsEnabled,
|
||||
command.alarmActions, command.okActions, command.undeterminedActions);
|
||||
return new Alarm(alarmId, command.name, command.description, command.severity,
|
||||
command.expression, command.state, command.actionsEnabled, command.alarmActions,
|
||||
command.okActions, command.undeterminedActions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches the alarm for the {@code tenantId} and {@code alarmId} to the state of the
|
||||
* {@code fields}.
|
||||
@@ -172,155 +87,54 @@ public class AlarmService {
|
||||
* @throws EntityNotFoundException if the alarm cannot be found
|
||||
* @throws InvalidEntityException if one of the actions cannot be found
|
||||
*/
|
||||
public Alarm patch(String tenantId, String alarmId, String name, String description,
|
||||
String severity, String expression, AlarmExpression alarmExpression, AlarmState state,
|
||||
Boolean enabled, List<String> alarmActions, List<String> okActions,
|
||||
List<String> undeterminedActions) {
|
||||
Alarm alarm =
|
||||
assertAlarmExists(tenantId, alarmId, alarmActions, okActions, undeterminedActions);
|
||||
name = name == null ? alarm.getName() : name;
|
||||
description = description == null ? alarm.getDescription() : description;
|
||||
expression = expression == null ? alarm.getExpression() : expression;
|
||||
severity = severity == null ? alarm.getSeverity() : severity;
|
||||
alarmExpression = alarmExpression == null ? AlarmExpression.of(expression) : alarmExpression;
|
||||
public Alarm patch(String tenantId, String alarmId, AlarmState state) {
|
||||
Alarm alarm = repo.findById(alarmId);
|
||||
state = state == null ? alarm.getState() : state;
|
||||
enabled = enabled == null ? alarm.isActionsEnabled() : enabled;
|
||||
|
||||
updateInternal(tenantId, alarmId, true, name, description, expression, severity,
|
||||
alarmExpression, alarm.getState(), state, enabled, alarmActions, okActions,
|
||||
undeterminedActions);
|
||||
|
||||
return new Alarm(alarmId, name, description, severity, expression, state, enabled,
|
||||
alarmActions == null ? alarm.getAlarmActions() : alarmActions,
|
||||
okActions == null ? alarm.getOkActions() : okActions,
|
||||
undeterminedActions == null ? alarm.getUndeterminedActions() : undeterminedActions);
|
||||
}
|
||||
|
||||
private void updateInternal(String tenantId, String alarmId, boolean patch, String name,
|
||||
String description, String expression, String severity, AlarmExpression alarmExpression,
|
||||
AlarmState oldState, AlarmState newState, Boolean enabled, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions) {
|
||||
SubExpressions subExpressions = subExpressionsFor(alarmId, alarmExpression);
|
||||
|
||||
try {
|
||||
LOG.debug("Updating alarm {} for tenant {}", name, tenantId);
|
||||
repo.update(tenantId, alarmId, patch, name, description, expression, severity, newState,
|
||||
enabled, subExpressions.oldAlarmSubExpressions.keySet(),
|
||||
subExpressions.changedSubExpressions, subExpressions.newAlarmSubExpressions,
|
||||
alarmActions, okActions, undeterminedActions);
|
||||
|
||||
// Notify interested parties of updated alarm
|
||||
String event =
|
||||
Serialization.toJson(new AlarmUpdatedEvent(tenantId, alarmId, name, description,
|
||||
expression, newState, oldState, enabled, subExpressions.oldAlarmSubExpressions,
|
||||
subExpressions.changedSubExpressions, subExpressions.unchangedSubExpressions,
|
||||
subExpressions.newAlarmSubExpressions));
|
||||
producer.send(new KeyedMessage<>(config.eventsTopic, tenantId, event));
|
||||
|
||||
// Notify interested parties of transitioned alarm state
|
||||
if (!oldState.equals(newState)) {
|
||||
event =
|
||||
Serialization.toJson(new AlarmStateTransitionedEvent(tenantId, alarmId, name,
|
||||
description, oldState, newState, enabled, stateChangeReasonFor(oldState, newState),
|
||||
System.currentTimeMillis() / 1000));
|
||||
producer.send(new KeyedMessage<>(config.alarmStateTransitionsTopic, tenantId, event));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.uncheck(e, "Error updating alarm for project / tenant %s", tenantId);
|
||||
}
|
||||
updateInternal(tenantId, alarm, alarm.getState(), state);
|
||||
alarm.setState(state);
|
||||
return alarm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an entry containing Maps of old, changed, and new sub expressions by comparing the
|
||||
* {@code alarmExpression} to the existing sub expressions for the {@code alarmId}.
|
||||
* Updates the alarmed metric for the {@code tenantId} and {@code alarmedMetricId} to the state of
|
||||
* the {@code command}.
|
||||
*
|
||||
* @throws EntityNotFoundException if the alarmed metric cannot be found
|
||||
*/
|
||||
SubExpressions subExpressionsFor(String alarmId, AlarmExpression alarmExpression) {
|
||||
BiMap<String, AlarmSubExpression> oldExpressions =
|
||||
HashBiMap.create(repo.findSubExpressions(alarmId));
|
||||
Set<AlarmSubExpression> oldSet = oldExpressions.inverse().keySet();
|
||||
Set<AlarmSubExpression> newSet = new HashSet<>(alarmExpression.getSubExpressions());
|
||||
|
||||
// Identify old or changed expressions
|
||||
Set<AlarmSubExpression> oldOrChangedExpressions =
|
||||
new HashSet<>(Sets.difference(oldSet, newSet));
|
||||
|
||||
// Identify new or changed expressions
|
||||
Set<AlarmSubExpression> newOrChangedExpressions =
|
||||
new HashSet<>(Sets.difference(newSet, oldSet));
|
||||
|
||||
// Find changed expressions
|
||||
Map<String, AlarmSubExpression> changedExpressions = new HashMap<>();
|
||||
for (Iterator<AlarmSubExpression> oldIt = oldOrChangedExpressions.iterator(); oldIt.hasNext();) {
|
||||
AlarmSubExpression oldExpr = oldIt.next();
|
||||
for (Iterator<AlarmSubExpression> newIt = newOrChangedExpressions.iterator(); newIt.hasNext();) {
|
||||
AlarmSubExpression newExpr = newIt.next();
|
||||
if (sameKeyFields(oldExpr, newExpr)) {
|
||||
oldIt.remove();
|
||||
newIt.remove();
|
||||
changedExpressions.put(oldExpressions.inverse().get(oldExpr), newExpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the list of unchanged expressions
|
||||
BiMap<String, AlarmSubExpression> unchangedExpressions = HashBiMap.create(oldExpressions);
|
||||
unchangedExpressions.values().removeAll(oldOrChangedExpressions);
|
||||
unchangedExpressions.keySet().removeAll(changedExpressions.keySet());
|
||||
|
||||
// Remove old sub expressions
|
||||
oldExpressions.values().retainAll(oldOrChangedExpressions);
|
||||
|
||||
// Create IDs for new expressions
|
||||
Map<String, AlarmSubExpression> newExpressions = new HashMap<>();
|
||||
for (AlarmSubExpression expression : newOrChangedExpressions)
|
||||
newExpressions.put(UUID.randomUUID().toString(), expression);
|
||||
|
||||
SubExpressions subExpressions = new SubExpressions();
|
||||
subExpressions.oldAlarmSubExpressions = oldExpressions;
|
||||
subExpressions.changedSubExpressions = changedExpressions;
|
||||
subExpressions.unchangedSubExpressions = unchangedExpressions;
|
||||
subExpressions.newAlarmSubExpressions = newExpressions;
|
||||
return subExpressions;
|
||||
public Alarm update(String tenantId, String alarmId, UpdateAlarmCommand command) {
|
||||
Alarm alarm = repo.findById(alarmId);
|
||||
updateInternal(tenantId, alarm, alarm.getState(), command.state);
|
||||
alarm.setState(command.state);
|
||||
return alarm;
|
||||
}
|
||||
|
||||
private String stateChangeReasonFor(AlarmState oldState, AlarmState newState) {
|
||||
return "Alarm state updated via API";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether all of the fields of {@code a} and {@code b} are the same except the operator
|
||||
* and threshold.
|
||||
*/
|
||||
private boolean sameKeyFields(AlarmSubExpression a, AlarmSubExpression b) {
|
||||
return a.getMetricDefinition().equals(b.getMetricDefinition())
|
||||
&& a.getFunction().equals(b.getFunction()) && a.getPeriod() == b.getPeriod()
|
||||
&& a.getPeriods() == b.getPeriods();
|
||||
}
|
||||
private void updateInternal(String tenantId, Alarm alarm, AlarmState oldState, AlarmState newState) {
|
||||
try {
|
||||
LOG.debug("Updating alarm {} for tenant {}", alarm.getId(), tenantId);
|
||||
repo.update(tenantId, alarm.getId(), newState);
|
||||
|
||||
/**
|
||||
* Asserts an alarm exists for the {@code alarmId} as well as the actions.
|
||||
*
|
||||
* @throws EntityNotFoundException if the alarm cannot be found
|
||||
*/
|
||||
private Alarm assertAlarmExists(String tenantId, String alarmId, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions) {
|
||||
Alarm alarm = repo.findById(tenantId, alarmId);
|
||||
assertActionsExist(tenantId, alarmActions, okActions, undeterminedActions);
|
||||
return alarm;
|
||||
}
|
||||
// Notify interested parties of updated alarm
|
||||
AlarmDefinition alarmDef = alarmDefRepo.findById(tenantId, alarm.getAlarmDefinitionId());
|
||||
String event =
|
||||
Serialization.toJson(new AlarmUpdatedEvent(alarm.getId(), alarm.getAlarmDefinitionId(),
|
||||
newState, oldState));
|
||||
producer.send(new KeyedMessage<>(config.eventsTopic, tenantId, event));
|
||||
|
||||
private void assertActionsExist(String tenantId, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions) {
|
||||
Set<String> actions = new HashSet<>();
|
||||
if (alarmActions != null)
|
||||
actions.addAll(alarmActions);
|
||||
if (okActions != null)
|
||||
actions.addAll(okActions);
|
||||
if (undeterminedActions != null)
|
||||
actions.addAll(undeterminedActions);
|
||||
if (!actions.isEmpty())
|
||||
for (String action : actions)
|
||||
if (!notificationMethodRepo.exists(tenantId, action))
|
||||
throw new InvalidEntityException("No notification method exists for action %s", action);
|
||||
// Notify interested parties of transitioned alarm state
|
||||
if (!oldState.equals(newState)) {
|
||||
event =
|
||||
Serialization.toJson(new AlarmStateTransitionedEvent(tenantId, alarm.getId(), alarmDef
|
||||
.getId(), alarm.getMetrics(), alarmDef.getName(), alarmDef.getDescription(),
|
||||
oldState, newState, alarmDef.isActionsEnabled(), stateChangeReasonFor(oldState,
|
||||
newState), System.currentTimeMillis() / 1000));
|
||||
producer.send(new KeyedMessage<>(config.alarmStateTransitionsTopic, tenantId, event));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.uncheck(e, "Error updating alarm for project / tenant %s", tenantId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ public class ApplicationModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(MetricService.class).in(Singleton.class);
|
||||
bind(AlarmDefinitionService.class).in(Singleton.class);
|
||||
bind(AlarmService.class).in(Singleton.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 com.hpcloud.mon.app.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import com.hpcloud.mon.app.validation.AlarmValidation;
|
||||
|
||||
public class CreateAlarmCommand {
|
||||
@NotEmpty
|
||||
public String name;
|
||||
public String description;
|
||||
@NotEmpty
|
||||
public String expression;
|
||||
public String severity;
|
||||
public List<String> alarmActions;
|
||||
public List<String> okActions;
|
||||
public List<String> undeterminedActions;
|
||||
|
||||
public CreateAlarmCommand() {
|
||||
this.severity = "LOW";
|
||||
}
|
||||
|
||||
public CreateAlarmCommand(String name, @Nullable String description, String expression,
|
||||
String severity, List<String> alarmActions, List<String> okActions,
|
||||
List<String> undeterminedActions) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.expression = expression;
|
||||
this.alarmActions = alarmActions;
|
||||
this.okActions = okActions;
|
||||
this.undeterminedActions = undeterminedActions;
|
||||
this.severity = severity == null ? "LOW" : severity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
CreateAlarmCommand other = (CreateAlarmCommand) obj;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equals(other.name))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
AlarmValidation.validate(name, description, severity, alarmActions, okActions,
|
||||
undeterminedActions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 com.hpcloud.mon.app.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import com.hpcloud.mon.app.validation.AlarmValidation;
|
||||
|
||||
public class CreateAlarmDefinitionCommand {
|
||||
@NotEmpty
|
||||
public String name;
|
||||
public String description;
|
||||
@NotEmpty
|
||||
public String expression;
|
||||
public List<String> matchBy;
|
||||
public String severity;
|
||||
public List<String> alarmActions;
|
||||
public List<String> okActions;
|
||||
public List<String> undeterminedActions;
|
||||
|
||||
public CreateAlarmDefinitionCommand() {
|
||||
this.severity = "LOW";
|
||||
}
|
||||
|
||||
public CreateAlarmDefinitionCommand(String name, @Nullable String description, String expression,
|
||||
List<String> matchBy, String severity, List<String> alarmActions, List<String> okActions,
|
||||
List<String> undeterminedActions) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.expression = expression;
|
||||
this.matchBy = matchBy;
|
||||
this.alarmActions = alarmActions;
|
||||
this.okActions = okActions;
|
||||
this.undeterminedActions = undeterminedActions;
|
||||
this.severity = severity == null ? "LOW" : severity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (!(obj instanceof CreateAlarmDefinitionCommand))
|
||||
return false;
|
||||
CreateAlarmDefinitionCommand other = (CreateAlarmDefinitionCommand) obj;
|
||||
if (alarmActions == null) {
|
||||
if (other.alarmActions != null)
|
||||
return false;
|
||||
} else if (!alarmActions.equals(other.alarmActions))
|
||||
return false;
|
||||
if (description == null) {
|
||||
if (other.description != null)
|
||||
return false;
|
||||
} else if (!description.equals(other.description))
|
||||
return false;
|
||||
if (expression == null) {
|
||||
if (other.expression != null)
|
||||
return false;
|
||||
} else if (!expression.equals(other.expression))
|
||||
return false;
|
||||
if (matchBy == null) {
|
||||
if (other.matchBy != null)
|
||||
return false;
|
||||
} else if (!matchBy.equals(other.matchBy))
|
||||
return false;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equals(other.name))
|
||||
return false;
|
||||
if (okActions == null) {
|
||||
if (other.okActions != null)
|
||||
return false;
|
||||
} else if (!okActions.equals(other.okActions))
|
||||
return false;
|
||||
if (severity == null) {
|
||||
if (other.severity != null)
|
||||
return false;
|
||||
} else if (!severity.equals(other.severity))
|
||||
return false;
|
||||
if (undeterminedActions == null) {
|
||||
if (other.undeterminedActions != null)
|
||||
return false;
|
||||
} else if (!undeterminedActions.equals(other.undeterminedActions))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((alarmActions == null) ? 0 : alarmActions.hashCode());
|
||||
result = prime * result + ((description == null) ? 0 : description.hashCode());
|
||||
result = prime * result + ((expression == null) ? 0 : expression.hashCode());
|
||||
result = prime * result + ((matchBy == null) ? 0 : matchBy.hashCode());
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
result = prime * result + ((okActions == null) ? 0 : okActions.hashCode());
|
||||
result = prime * result + ((severity == null) ? 0 : severity.hashCode());
|
||||
result = prime * result + ((undeterminedActions == null) ? 0 : undeterminedActions.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
AlarmValidation.validate(name, description, severity, alarmActions, okActions,
|
||||
undeterminedActions);
|
||||
}
|
||||
}
|
||||
@@ -13,26 +13,17 @@
|
||||
*/
|
||||
package com.hpcloud.mon.app.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
|
||||
public class UpdateAlarmCommand extends CreateAlarmCommand {
|
||||
public class UpdateAlarmCommand {
|
||||
@NotNull
|
||||
public AlarmState state;
|
||||
@NotNull
|
||||
public Boolean actionsEnabled;
|
||||
|
||||
public UpdateAlarmCommand() {}
|
||||
|
||||
public UpdateAlarmCommand(String name, @Nullable String description, String severity,
|
||||
String expression, AlarmState state, boolean enabled, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions) {
|
||||
super(name, description, severity, expression, alarmActions, okActions, undeterminedActions);
|
||||
public UpdateAlarmCommand(AlarmState state) {
|
||||
this.state = state;
|
||||
this.actionsEnabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 com.hpcloud.mon.app.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class UpdateAlarmDefinitionCommand extends CreateAlarmDefinitionCommand {
|
||||
@NotNull
|
||||
public Boolean actionsEnabled;
|
||||
|
||||
public UpdateAlarmDefinitionCommand() {}
|
||||
|
||||
public UpdateAlarmDefinitionCommand(String name, @Nullable String description, String expression,
|
||||
List<String> matchBy, String severity, boolean enabled, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions) {
|
||||
super(name, description, expression, matchBy, severity, alarmActions, okActions,
|
||||
undeterminedActions);
|
||||
this.actionsEnabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!super.equals(obj))
|
||||
return false;
|
||||
if (!(obj instanceof UpdateAlarmDefinitionCommand))
|
||||
return false;
|
||||
UpdateAlarmDefinitionCommand other = (UpdateAlarmDefinitionCommand) obj;
|
||||
if (actionsEnabled == null) {
|
||||
if (other.actionsEnabled != null)
|
||||
return false;
|
||||
} else if (!actionsEnabled.equals(other.actionsEnabled))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + ((actionsEnabled == null) ? 0 : actionsEnabled.hashCode());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -18,120 +18,31 @@ import java.util.List;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmExpression;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.common.model.metric.MetricDefinition;
|
||||
import com.hpcloud.mon.domain.common.AbstractEntity;
|
||||
import com.hpcloud.mon.domain.model.common.Link;
|
||||
import com.hpcloud.mon.domain.model.common.Linked;
|
||||
import com.wordnik.swagger.annotations.ApiModel;
|
||||
import com.wordnik.swagger.annotations.ApiModelProperty;
|
||||
|
||||
@ApiModel(value = "An alarm is a devops's best friend")
|
||||
@XmlRootElement(name = "Alarm")
|
||||
public class Alarm extends AbstractEntity implements Linked {
|
||||
private List<Link> links;
|
||||
private String name;
|
||||
private String description = "";
|
||||
private String expression;
|
||||
private Object expressionData;
|
||||
private String alarmDefinitionId;
|
||||
private List<MetricDefinition> metrics;
|
||||
private AlarmState state;
|
||||
private String severity;
|
||||
private boolean actionsEnabled;
|
||||
private List<String> alarmActions;
|
||||
private List<String> okActions;
|
||||
private List<String> undeterminedActions;
|
||||
|
||||
public Alarm() {}
|
||||
|
||||
public Alarm(String id, String name, String description, String severity, String expression,
|
||||
AlarmState state, boolean actionsEnabled, List<String> alarmActions, List<String> okActions,
|
||||
List<String> undeterminedActions) {
|
||||
public Alarm(String id, String alarmDefinitionId, String metricName,
|
||||
List<MetricDefinition> metrics, AlarmState state) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
setDescription(description);
|
||||
setSeverity(severity);
|
||||
setExpression(expression);
|
||||
setMetrics(metrics);
|
||||
setState(state);
|
||||
setActionsEnabled(actionsEnabled);
|
||||
setAlarmActions(alarmActions);
|
||||
setOkActions(okActions);
|
||||
setUndeterminedActions(undeterminedActions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!super.equals(obj))
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Alarm other = (Alarm) obj;
|
||||
if (alarmActions == null) {
|
||||
if (other.alarmActions != null)
|
||||
return false;
|
||||
} else if (!alarmActions.equals(other.alarmActions))
|
||||
return false;
|
||||
if (description == null) {
|
||||
if (other.description != null)
|
||||
return false;
|
||||
} else if (!description.equals(other.description))
|
||||
return false;
|
||||
if (severity == null) {
|
||||
if (other.severity != null)
|
||||
return false;
|
||||
} else if (!severity.equals(other.severity))
|
||||
return false;
|
||||
if (actionsEnabled != other.actionsEnabled)
|
||||
return false;
|
||||
if (expression == null) {
|
||||
if (other.expression != null)
|
||||
return false;
|
||||
} else if (!expression.equals(other.expression))
|
||||
return false;
|
||||
if (links == null) {
|
||||
if (other.links != null)
|
||||
return false;
|
||||
} else if (!links.equals(other.links))
|
||||
return false;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equals(other.name))
|
||||
return false;
|
||||
if (okActions == null) {
|
||||
if (other.okActions != null)
|
||||
return false;
|
||||
} else if (!okActions.equals(other.okActions))
|
||||
return false;
|
||||
if (state != other.state)
|
||||
return false;
|
||||
if (undeterminedActions == null) {
|
||||
if (other.undeterminedActions != null)
|
||||
return false;
|
||||
} else if (!undeterminedActions.equals(other.undeterminedActions))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<String> getAlarmActions() {
|
||||
return alarmActions;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getSeverity() {
|
||||
return severity;
|
||||
}
|
||||
|
||||
public String getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
public Object getExpressionData() {
|
||||
return expressionData;
|
||||
public String getAlarmDefinitionId() {
|
||||
return alarmDefinitionId;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
@@ -142,62 +53,12 @@ public class Alarm extends AbstractEntity implements Linked {
|
||||
return links;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<String> getOkActions() {
|
||||
return okActions;
|
||||
}
|
||||
|
||||
public AlarmState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public List<String> getUndeterminedActions() {
|
||||
return undeterminedActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + ((alarmActions == null) ? 0 : alarmActions.hashCode());
|
||||
result = prime * result + ((description == null) ? 0 : description.hashCode());
|
||||
result = prime * result + ((severity == null) ? 0 : severity.hashCode());
|
||||
result = prime * result + (actionsEnabled ? 1231 : 1237);
|
||||
result = prime * result + ((expression == null) ? 0 : expression.hashCode());
|
||||
result = prime * result + ((links == null) ? 0 : links.hashCode());
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
result = prime * result + ((okActions == null) ? 0 : okActions.hashCode());
|
||||
result = prime * result + ((state == null) ? 0 : state.hashCode());
|
||||
result = prime * result + ((undeterminedActions == null) ? 0 : undeterminedActions.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isActionsEnabled() {
|
||||
return actionsEnabled;
|
||||
}
|
||||
|
||||
public void setActionsEnabled(boolean actionsEnabled) {
|
||||
this.actionsEnabled = actionsEnabled;
|
||||
}
|
||||
|
||||
public void setAlarmActions(List<String> alarmActions) {
|
||||
this.alarmActions = alarmActions;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description == null ? "" : description;
|
||||
}
|
||||
|
||||
public void setExpression(String expression) {
|
||||
this.expression = expression;
|
||||
setExpressionData(AlarmExpression.of(expression).getExpressionTree());
|
||||
}
|
||||
|
||||
public void setExpressionData(Object expressionData) {
|
||||
this.expressionData = expressionData;
|
||||
public void setAlarmDefinitionId(String alarmDefinitionId) {
|
||||
this.alarmDefinitionId = alarmDefinitionId;
|
||||
}
|
||||
|
||||
@XmlElement(name = "id")
|
||||
@@ -211,28 +72,15 @@ public class Alarm extends AbstractEntity implements Linked {
|
||||
this.links = links;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setOkActions(List<String> okActions) {
|
||||
this.okActions = okActions;
|
||||
}
|
||||
|
||||
public void setState(AlarmState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void setSeverity(String severity) {
|
||||
this.severity = severity;
|
||||
public List<MetricDefinition> getMetrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public void setUndeterminedActions(List<String> undeterminedActions) {
|
||||
this.undeterminedActions = undeterminedActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Alarm [name=%s]", name);
|
||||
public void setMetrics(List<MetricDefinition> metrics) {
|
||||
this.metrics = metrics;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 com.hpcloud.mon.domain.model.alarm;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmSubExpression;
|
||||
import com.hpcloud.mon.common.model.metric.MetricDefinition;
|
||||
import com.hpcloud.mon.domain.exception.EntityNotFoundException;
|
||||
|
||||
/**
|
||||
* Repository for alarms.
|
||||
*/
|
||||
public interface AlarmRepository {
|
||||
/**
|
||||
* Creates and returns a new alarm for the criteria.
|
||||
* Deletes all alarms associated with the {@code id}.
|
||||
*/
|
||||
Alarm create(String tenantId, String id, String name, String description, String severity,
|
||||
String expression, Map<String, AlarmSubExpression> subExpressions, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions);
|
||||
|
||||
/**
|
||||
* @throws EntityNotFoundException if an alarm cannot be found for the {@code alarmId}
|
||||
*/
|
||||
void deleteById(String tenantId, String alarmId);
|
||||
|
||||
/**
|
||||
* Returns true if an alarm exists for the given criteria, else false.
|
||||
*/
|
||||
boolean exists(String tenantId, String name);
|
||||
void deleteById(String id);
|
||||
|
||||
/**
|
||||
* Returns alarms for the given criteria.
|
||||
*/
|
||||
List<Alarm> find(String tenantId, String name, Map<String, String> dimensions, String state);
|
||||
List<Alarm> find(String tenantId, String alarmDefId, String metricName,
|
||||
Map<String, String> metricDimensions, AlarmState state);
|
||||
|
||||
/**
|
||||
* @throws EntityNotFoundException if an alarm cannot be found for the {@code alarmId}
|
||||
* @throws EntityNotFoundException if an alarm cannot be found for the {@code id}
|
||||
*/
|
||||
Alarm findById(String tenantId, String alarmId);
|
||||
Alarm findById(String id);
|
||||
|
||||
/**
|
||||
* Returns the sub-alarm Ids for the {@code alarmId}.
|
||||
*/
|
||||
Map<String, MetricDefinition> findSubAlarmMetricDefinitions(String alarmId);
|
||||
|
||||
/**
|
||||
* Returns the sub expressions for the {@code alarmId}.
|
||||
*/
|
||||
Map<String, AlarmSubExpression> findSubExpressions(String alarmId);
|
||||
List<MetricDefinition> findMetrics(String alarmId);
|
||||
|
||||
/**
|
||||
* Updates and returns an alarm for the criteria.
|
||||
*/
|
||||
void update(String tenantId, String id, boolean patch, String name, String description,
|
||||
String expression, String severity, AlarmState state, boolean enabled,
|
||||
Collection<String> oldSubAlarmIds, Map<String, AlarmSubExpression> changedSubAlarms,
|
||||
Map<String, AlarmSubExpression> newSubAlarms, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions);
|
||||
void update(String tenantId, String id, AlarmState state);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 com.hpcloud.mon.domain.model.alarmdefinition;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmExpression;
|
||||
import com.hpcloud.mon.domain.common.AbstractEntity;
|
||||
import com.hpcloud.mon.domain.model.common.Link;
|
||||
import com.hpcloud.mon.domain.model.common.Linked;
|
||||
import com.wordnik.swagger.annotations.ApiModel;
|
||||
import com.wordnik.swagger.annotations.ApiModelProperty;
|
||||
|
||||
@ApiModel(value = "Defines an alarm")
|
||||
@XmlRootElement(name = "Alarm definition")
|
||||
public class AlarmDefinition extends AbstractEntity implements Linked {
|
||||
private List<Link> links;
|
||||
private String name;
|
||||
private String description = "";
|
||||
private String expression;
|
||||
private Object expressionData;
|
||||
private List<String> matchBy;
|
||||
private String severity;
|
||||
private boolean actionsEnabled;
|
||||
private List<String> alarmActions;
|
||||
private List<String> okActions;
|
||||
private List<String> undeterminedActions;
|
||||
|
||||
public AlarmDefinition() {}
|
||||
|
||||
public AlarmDefinition(String id, String name, String description, String severity,
|
||||
String expression, List<String> matchBy, boolean actionsEnabled, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
setDescription(description);
|
||||
setSeverity(severity);
|
||||
setExpression(expression);
|
||||
setMatchBy(matchBy);
|
||||
setActionsEnabled(actionsEnabled);
|
||||
setAlarmActions(alarmActions);
|
||||
setOkActions(okActions);
|
||||
setUndeterminedActions(undeterminedActions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!super.equals(obj))
|
||||
return false;
|
||||
if (!(obj instanceof AlarmDefinition))
|
||||
return false;
|
||||
AlarmDefinition other = (AlarmDefinition) obj;
|
||||
if (actionsEnabled != other.actionsEnabled)
|
||||
return false;
|
||||
if (alarmActions == null) {
|
||||
if (other.alarmActions != null)
|
||||
return false;
|
||||
} else if (!alarmActions.equals(other.alarmActions))
|
||||
return false;
|
||||
if (description == null) {
|
||||
if (other.description != null)
|
||||
return false;
|
||||
} else if (!description.equals(other.description))
|
||||
return false;
|
||||
if (expression == null) {
|
||||
if (other.expression != null)
|
||||
return false;
|
||||
} else if (!expression.equals(other.expression))
|
||||
return false;
|
||||
if (expressionData == null) {
|
||||
if (other.expressionData != null)
|
||||
return false;
|
||||
} else if (!expressionData.equals(other.expressionData))
|
||||
return false;
|
||||
if (links == null) {
|
||||
if (other.links != null)
|
||||
return false;
|
||||
} else if (!links.equals(other.links))
|
||||
return false;
|
||||
if (matchBy == null) {
|
||||
if (other.matchBy != null)
|
||||
return false;
|
||||
} else if (!matchBy.equals(other.matchBy))
|
||||
return false;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equals(other.name))
|
||||
return false;
|
||||
if (okActions == null) {
|
||||
if (other.okActions != null)
|
||||
return false;
|
||||
} else if (!okActions.equals(other.okActions))
|
||||
return false;
|
||||
if (severity == null) {
|
||||
if (other.severity != null)
|
||||
return false;
|
||||
} else if (!severity.equals(other.severity))
|
||||
return false;
|
||||
if (undeterminedActions == null) {
|
||||
if (other.undeterminedActions != null)
|
||||
return false;
|
||||
} else if (!undeterminedActions.equals(other.undeterminedActions))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<String> getAlarmActions() {
|
||||
return alarmActions;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
public Object getExpressionData() {
|
||||
return expressionData;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<Link> getLinks() {
|
||||
return links;
|
||||
}
|
||||
|
||||
public List<String> getMatchBy() {
|
||||
return matchBy;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<String> getOkActions() {
|
||||
return okActions;
|
||||
}
|
||||
|
||||
public String getSeverity() {
|
||||
return severity;
|
||||
}
|
||||
|
||||
public List<String> getUndeterminedActions() {
|
||||
return undeterminedActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + (actionsEnabled ? 1231 : 1237);
|
||||
result = prime * result + ((alarmActions == null) ? 0 : alarmActions.hashCode());
|
||||
result = prime * result + ((description == null) ? 0 : description.hashCode());
|
||||
result = prime * result + ((expression == null) ? 0 : expression.hashCode());
|
||||
result = prime * result + ((expressionData == null) ? 0 : expressionData.hashCode());
|
||||
result = prime * result + ((links == null) ? 0 : links.hashCode());
|
||||
result = prime * result + ((matchBy == null) ? 0 : matchBy.hashCode());
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
result = prime * result + ((okActions == null) ? 0 : okActions.hashCode());
|
||||
result = prime * result + ((severity == null) ? 0 : severity.hashCode());
|
||||
result = prime * result + ((undeterminedActions == null) ? 0 : undeterminedActions.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isActionsEnabled() {
|
||||
return actionsEnabled;
|
||||
}
|
||||
|
||||
public void setActionsEnabled(boolean actionsEnabled) {
|
||||
this.actionsEnabled = actionsEnabled;
|
||||
}
|
||||
|
||||
public void setAlarmActions(List<String> alarmActions) {
|
||||
this.alarmActions = alarmActions;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description == null ? "" : description;
|
||||
}
|
||||
|
||||
public void setExpression(String expression) {
|
||||
this.expression = expression;
|
||||
setExpressionData(AlarmExpression.of(expression).getExpressionTree());
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public void setExpressionData(Object expressionData) {
|
||||
this.expressionData = expressionData;
|
||||
}
|
||||
|
||||
@XmlElement(name = "id")
|
||||
@ApiModelProperty(value = "Alarm definition ID")
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLinks(List<Link> links) {
|
||||
this.links = links;
|
||||
}
|
||||
|
||||
public void setMatchBy(List<String> matchBy) {
|
||||
this.matchBy = matchBy == null ? Collections.<String>emptyList() : matchBy;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setOkActions(List<String> okActions) {
|
||||
this.okActions = okActions;
|
||||
}
|
||||
|
||||
public void setSeverity(String severity) {
|
||||
this.severity = severity;
|
||||
}
|
||||
|
||||
public void setUndeterminedActions(List<String> undeterminedActions) {
|
||||
this.undeterminedActions = undeterminedActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("AlarmDefinition [name=%s]", name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 com.hpcloud.mon.domain.model.alarmdefinition;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmSubExpression;
|
||||
import com.hpcloud.mon.common.model.metric.MetricDefinition;
|
||||
import com.hpcloud.mon.domain.exception.EntityNotFoundException;
|
||||
|
||||
/**
|
||||
* Repository for alarm definitions.
|
||||
*/
|
||||
public interface AlarmDefinitionRepository {
|
||||
/**
|
||||
* Creates and returns a new alarm definition for the criteria.
|
||||
*/
|
||||
AlarmDefinition create(String tenantId, String id, String name, String description,
|
||||
String severity, String expression, Map<String, AlarmSubExpression> subExpressions,
|
||||
List<String> matchBy, List<String> alarmActions, List<String> okActions,
|
||||
List<String> undeterminedActions);
|
||||
|
||||
/**
|
||||
* @throws EntityNotFoundException if an alarm definition cannot be found for the
|
||||
* {@code alarmDefId}
|
||||
*/
|
||||
void deleteById(String tenantId, String alarmDefId);
|
||||
|
||||
/**
|
||||
* Returns true if an alarm exists for the given criteria, else false.
|
||||
*/
|
||||
boolean exists(String tenantId, String name);
|
||||
|
||||
/**
|
||||
* Returns alarms for the given criteria.
|
||||
*/
|
||||
List<AlarmDefinition> find(String tenantId, String name, Map<String, String> dimensions);
|
||||
|
||||
/**
|
||||
* @throws EntityNotFoundException if an alarm cannot be found for the {@code alarmDefId}
|
||||
*/
|
||||
AlarmDefinition findById(String tenantId, String alarmDefId);
|
||||
|
||||
/**
|
||||
* Returns the sub-alarm Ids for the {@code alarmDefId}.
|
||||
*/
|
||||
Map<String, MetricDefinition> findSubAlarmMetricDefinitions(String alarmDefId);
|
||||
|
||||
/**
|
||||
* Returns the sub expressions for the {@code alarmDefId}.
|
||||
*/
|
||||
Map<String, AlarmSubExpression> findSubExpressions(String alarmDefId);
|
||||
|
||||
/**
|
||||
* Updates and returns an alarm definition for the criteria.
|
||||
*/
|
||||
void update(String tenantId, String id, boolean patch, String name, String description,
|
||||
String expression, List<String> matchBy, String severity, boolean actionsEnabled,
|
||||
Collection<String> oldSubAlarmIds, Map<String, AlarmSubExpression> changedSubAlarms,
|
||||
Map<String, AlarmSubExpression> newSubAlarms, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions);
|
||||
}
|
||||
@@ -13,12 +13,16 @@
|
||||
*/
|
||||
package com.hpcloud.mon.domain.model.alarmstatehistory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.common.model.metric.MetricDefinition;
|
||||
|
||||
public class AlarmStateHistory {
|
||||
private String alarmId;
|
||||
private List<MetricDefinition> metrics;
|
||||
private AlarmState oldState;
|
||||
private AlarmState newState;
|
||||
private String reason;
|
||||
@@ -27,9 +31,10 @@ public class AlarmStateHistory {
|
||||
|
||||
public AlarmStateHistory() {}
|
||||
|
||||
public AlarmStateHistory(String alarmId, AlarmState oldState, AlarmState newState, String reason,
|
||||
String reasonData, DateTime timestamp) {
|
||||
public AlarmStateHistory(String alarmId, List<MetricDefinition> metrics, AlarmState oldState,
|
||||
AlarmState newState, String reason, String reasonData, DateTime timestamp) {
|
||||
this.alarmId = alarmId;
|
||||
this.setMetrics(metrics);
|
||||
this.oldState = oldState;
|
||||
this.newState = newState;
|
||||
this.reason = reason;
|
||||
@@ -43,7 +48,7 @@ public class AlarmStateHistory {
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
if (!(obj instanceof AlarmStateHistory))
|
||||
return false;
|
||||
AlarmStateHistory other = (AlarmStateHistory) obj;
|
||||
if (alarmId == null) {
|
||||
@@ -51,6 +56,11 @@ public class AlarmStateHistory {
|
||||
return false;
|
||||
} else if (!alarmId.equals(other.alarmId))
|
||||
return false;
|
||||
if (metrics == null) {
|
||||
if (other.metrics != null)
|
||||
return false;
|
||||
} else if (!metrics.equals(other.metrics))
|
||||
return false;
|
||||
if (newState != other.newState)
|
||||
return false;
|
||||
if (oldState != other.oldState)
|
||||
@@ -77,6 +87,10 @@ public class AlarmStateHistory {
|
||||
return alarmId;
|
||||
}
|
||||
|
||||
public List<MetricDefinition> getMetrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public AlarmState getNewState() {
|
||||
return newState;
|
||||
}
|
||||
@@ -102,6 +116,7 @@ public class AlarmStateHistory {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((alarmId == null) ? 0 : alarmId.hashCode());
|
||||
result = prime * result + ((metrics == null) ? 0 : metrics.hashCode());
|
||||
result = prime * result + ((newState == null) ? 0 : newState.hashCode());
|
||||
result = prime * result + ((oldState == null) ? 0 : oldState.hashCode());
|
||||
result = prime * result + ((reason == null) ? 0 : reason.hashCode());
|
||||
@@ -114,6 +129,10 @@ public class AlarmStateHistory {
|
||||
this.alarmId = alarmId;
|
||||
}
|
||||
|
||||
public void setMetrics(List<MetricDefinition> metrics) {
|
||||
this.metrics = metrics;
|
||||
}
|
||||
|
||||
public void setNewState(AlarmState newState) {
|
||||
this.newState = newState;
|
||||
}
|
||||
@@ -136,9 +155,8 @@ public class AlarmStateHistory {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String
|
||||
.format(
|
||||
"AlarmStateHistory [alarmId=%s, oldState=%s, newState=%s, reason=%s, reasonData=%s, timestamp=%s]",
|
||||
alarmId, oldState, newState, reason, reasonData, timestamp);
|
||||
return "AlarmStateHistory [alarmId=" + alarmId + ", metrics=" + metrics + ", oldState="
|
||||
+ oldState + ", newState=" + newState + ", reason=" + reason + ", reasonData=" + reasonData
|
||||
+ ", timestamp=" + timestamp + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.google.inject.Provides;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.hpcloud.mon.MonApiConfiguration;
|
||||
import com.hpcloud.mon.domain.model.alarm.AlarmRepository;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinitionRepository;
|
||||
import com.hpcloud.mon.domain.model.alarmstatehistory.AlarmStateHistoryRepository;
|
||||
import com.hpcloud.mon.domain.model.measurement.MeasurementRepository;
|
||||
import com.hpcloud.mon.domain.model.metric.MetricDefinitionRepository;
|
||||
@@ -32,6 +33,7 @@ import com.hpcloud.mon.infrastructure.persistence.influxdb.AlarmStateHistoryInfl
|
||||
import com.hpcloud.mon.infrastructure.persistence.influxdb.MeasurementInfluxDbRepositoryImpl;
|
||||
import com.hpcloud.mon.infrastructure.persistence.influxdb.MetricDefinitionInfluxDbRepositoryImpl;
|
||||
import com.hpcloud.mon.infrastructure.persistence.influxdb.StatisticInfluxDbRepositoryImpl;
|
||||
import com.hpcloud.mon.infrastructure.persistence.mysql.AlarmDefinitionMySqlRepositoryImpl;
|
||||
import com.hpcloud.mon.infrastructure.persistence.mysql.AlarmMySqlRepositoryImpl;
|
||||
import com.hpcloud.mon.infrastructure.persistence.mysql.NotificationMethodMySqlRepositoryImpl;
|
||||
import com.hpcloud.mon.infrastructure.persistence.vertica.AlarmStateHistoryVerticaRepositoryImpl;
|
||||
@@ -53,6 +55,8 @@ public class InfrastructureModule extends AbstractModule {
|
||||
protected void configure() {
|
||||
// Bind repositories
|
||||
bind(AlarmRepository.class).to(AlarmMySqlRepositoryImpl.class).in(Singleton.class);
|
||||
bind(AlarmDefinitionRepository.class).to(AlarmDefinitionMySqlRepositoryImpl.class).in(
|
||||
Singleton.class);
|
||||
if (config.databaseConfiguration.getDatabaseType().trim().toLowerCase().equals("vertica")) {
|
||||
bind(AlarmStateHistoryRepository.class).to(AlarmStateHistoryVerticaRepositoryImpl.class).in(
|
||||
Singleton.class);
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
*/
|
||||
package com.hpcloud.mon.infrastructure.persistence;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -36,4 +38,19 @@ public final class DimensionQueries {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, String> dimensionsFor(String dimensionSet) {
|
||||
Map<String, String> dimensions = Collections.emptyMap();
|
||||
|
||||
if (dimensionSet != null) {
|
||||
dimensions = new HashMap<String, String>();
|
||||
for (String kvStr : dimensionSet.split(",")) {
|
||||
String[] kv = kvStr.split("=");
|
||||
if (kv.length > 1)
|
||||
dimensions.put(kv[0], kv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return dimensions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,14 +13,15 @@
|
||||
*/
|
||||
package com.hpcloud.mon.infrastructure.persistence.influxdb;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.hpcloud.mon.MonApiConfiguration;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.domain.model.alarmstatehistory.AlarmStateHistory;
|
||||
import com.hpcloud.mon.domain.model.alarmstatehistory.AlarmStateHistoryRepository;
|
||||
import com.hpcloud.mon.infrastructure.persistence.DimensionQueries;
|
||||
import com.hpcloud.mon.infrastructure.persistence.SubAlarmQueries;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.influxdb.InfluxDB;
|
||||
import org.influxdb.dto.Serie;
|
||||
@@ -33,35 +34,42 @@ import org.skife.jdbi.v2.util.StringMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Named;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.google.inject.Inject;
|
||||
import com.hpcloud.mon.MonApiConfiguration;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.common.model.metric.MetricDefinition;
|
||||
import com.hpcloud.mon.domain.model.alarmstatehistory.AlarmStateHistory;
|
||||
import com.hpcloud.mon.domain.model.alarmstatehistory.AlarmStateHistoryRepository;
|
||||
import com.hpcloud.mon.infrastructure.persistence.DimensionQueries;
|
||||
import com.hpcloud.mon.infrastructure.persistence.SubAlarmQueries;
|
||||
|
||||
public class AlarmStateHistoryInfluxDbRepositoryImpl implements AlarmStateHistoryRepository {
|
||||
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
private static final TypeReference<List<MetricDefinition>> METRICS_TYPE =
|
||||
new TypeReference<List<MetricDefinition>>() {};
|
||||
private static final Logger logger = LoggerFactory
|
||||
.getLogger(AlarmStateHistoryInfluxDbRepositoryImpl.class);
|
||||
private static final String FIND_ALARMS_SQL =
|
||||
"select distinct ad.id from alarm_definition as ad "
|
||||
+ "join sub_alarm_definition sad on ad.id = sad.alarm_definition_id "
|
||||
+ "left outer join sub_alarm_definition_dimension dim on sad.id = dim.sub_alarm_definition_id%s "
|
||||
+ "where ad.tenant_id = :tenantId and ad.deleted_at is NULL";
|
||||
|
||||
private final MonApiConfiguration config;
|
||||
private final InfluxDB influxDB;
|
||||
private final DBI mysql;
|
||||
|
||||
private static final String FIND_ALARMS_SQL = "select distinct a.id from alarm as a " + "join"
|
||||
+ " sub_alarm sa on a.id = sa.alarm_id "
|
||||
+ "left outer join sub_alarm_dimension dim on "
|
||||
+ "sa.id = dim.sub_alarm_id%s "
|
||||
+ "where a.tenant_id = :tenantId and a.deleted_at is "
|
||||
+ "NULL";
|
||||
static {
|
||||
OBJECT_MAPPER
|
||||
.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public AlarmStateHistoryInfluxDbRepositoryImpl(@Named("mysql") DBI mysql,
|
||||
MonApiConfiguration config, InfluxDB influxDB) {
|
||||
MonApiConfiguration config, InfluxDB influxDB) {
|
||||
this.mysql = mysql;
|
||||
this.config = config;
|
||||
this.influxDB = influxDB;
|
||||
@@ -69,25 +77,20 @@ public class AlarmStateHistoryInfluxDbRepositoryImpl implements AlarmStateHistor
|
||||
|
||||
@Override
|
||||
public List<AlarmStateHistory> findById(String tenantId, String alarmId) throws Exception {
|
||||
|
||||
// InfluxDB orders queries by time stamp desc by default.
|
||||
String query = buildQueryForFindById(tenantId, alarmId);
|
||||
return queryInfluxDBForAlarmStateHistory(query);
|
||||
}
|
||||
|
||||
String buildQueryForFindById(String tenantId, String alarmId) throws Exception {
|
||||
|
||||
return String.format("select alarm_id, old_state, new_state, reason, reason_data "
|
||||
+ "from alarm_state_history "
|
||||
+ "where tenant_id = '%1$s' and alarm_id = '%2$s'",
|
||||
Utils.SQLSanitizer.sanitize(tenantId),
|
||||
Utils.SQLSanitizer.sanitize(alarmId));
|
||||
return String.format("select alarm_id, metrics, old_state, new_state, reason, reason_data "
|
||||
+ "from alarm_state_history where tenant_id = '%1$s' and alarm_id = '%2$s'",
|
||||
Utils.SQLSanitizer.sanitize(tenantId), Utils.SQLSanitizer.sanitize(alarmId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<AlarmStateHistory> find(String tenantId, Map<String, String> dimensions,
|
||||
DateTime startTime, @Nullable DateTime endTime)
|
||||
throws Exception {
|
||||
DateTime startTime, @Nullable DateTime endTime) throws Exception {
|
||||
|
||||
List<String> alarmIds = null;
|
||||
// Find alarm Ids for dimensions
|
||||
@@ -116,9 +119,9 @@ public class AlarmStateHistoryInfluxDbRepositoryImpl implements AlarmStateHistor
|
||||
}
|
||||
|
||||
String buildQueryForFind(String tenantId, String timePart, String alarmsPart) throws Exception {
|
||||
return String.format("select alarm_id, old_state, new_state, reason, reason_data "
|
||||
+ "from alarm_state_history " + "where tenant_id = '%1$s' %2$s %3$s",
|
||||
Utils.SQLSanitizer.sanitize(tenantId), timePart, alarmsPart);
|
||||
return String.format("select alarm_id, metrics, old_state, new_state, reason, reason_data "
|
||||
+ "from alarm_state_history where tenant_id = '%1$s' %2$s %3$s",
|
||||
Utils.SQLSanitizer.sanitize(tenantId), timePart, alarmsPart);
|
||||
}
|
||||
|
||||
String buildAlarmsPart(List<String> alarmIds) {
|
||||
@@ -138,17 +141,16 @@ public class AlarmStateHistoryInfluxDbRepositoryImpl implements AlarmStateHistor
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<AlarmStateHistory> queryInfluxDBForAlarmStateHistory(String query) {
|
||||
|
||||
logger.debug("Query string: {}", query);
|
||||
|
||||
List<Serie> result;
|
||||
try {
|
||||
result =
|
||||
this.influxDB.Query(this.config.influxDB.getName(), query, TimeUnit.MILLISECONDS);
|
||||
result = this.influxDB.Query(this.config.influxDB.getName(), query, TimeUnit.MILLISECONDS);
|
||||
} catch (Exception e) {
|
||||
if (e.getMessage().startsWith(Utils.COULD_NOT_LOOK_UP_COLUMNS_EXC_MSG)) {
|
||||
return new LinkedList();
|
||||
return new LinkedList<>();
|
||||
} else {
|
||||
logger.error("Failed to get data from InfluxDB", e);
|
||||
throw e;
|
||||
@@ -161,18 +163,25 @@ public class AlarmStateHistoryInfluxDbRepositoryImpl implements AlarmStateHistor
|
||||
for (Serie serie : result) {
|
||||
final String[] colNames = serie.getColumns();
|
||||
final List<Map<String, Object>> rows = serie.getRows();
|
||||
for (Map<String, Object> row : rows) {
|
||||
|
||||
for (Map<String, Object> row : rows) {
|
||||
AlarmStateHistory alarmStateHistory = new AlarmStateHistory();
|
||||
// Time is always in position 0.
|
||||
Double timeDouble = (Double) row.get(colNames[0]);
|
||||
alarmStateHistory.setTimestamp(new DateTime(timeDouble.longValue(), DateTimeZone.UTC));
|
||||
// Sequence_number is always in position 1.
|
||||
alarmStateHistory.setAlarmId((String) row.get(colNames[2]));
|
||||
alarmStateHistory.setNewState(AlarmState.valueOf((String) row.get(colNames[3])));
|
||||
alarmStateHistory.setOldState(AlarmState.valueOf((String) row.get(colNames[4])));
|
||||
alarmStateHistory.setReason((String) row.get(colNames[5]));
|
||||
alarmStateHistory.setReasonData((String) row.get(colNames[6]));
|
||||
try {
|
||||
alarmStateHistory.setMetrics((List<MetricDefinition>) OBJECT_MAPPER.readValue(
|
||||
(String) row.get(colNames[3]), METRICS_TYPE));
|
||||
} catch (Exception ignore) {
|
||||
alarmStateHistory.setMetrics(Collections.<MetricDefinition>emptyList());
|
||||
}
|
||||
|
||||
alarmStateHistory.setNewState(AlarmState.valueOf((String) row.get(colNames[4])));
|
||||
alarmStateHistory.setOldState(AlarmState.valueOf((String) row.get(colNames[5])));
|
||||
alarmStateHistory.setReason((String) row.get(colNames[6]));
|
||||
alarmStateHistory.setReasonData((String) row.get(colNames[7]));
|
||||
|
||||
alarmStateHistoryList.add(alarmStateHistory);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class MetricDefinitionInfluxDbRepositoryImpl implements MetricDefinitionR
|
||||
private List<MetricDefinition> buildMetricDefList(List<Serie> result) throws Exception {
|
||||
List<MetricDefinition> metricDefinitionList = new ArrayList<>();
|
||||
for (Serie serie : result) {
|
||||
for (Map point : serie.getRows()) {
|
||||
for (Map<String,Object> point : serie.getRows()) {
|
||||
|
||||
Utils.SerieNameDecoder serieNameDecoder;
|
||||
try {
|
||||
|
||||
@@ -176,7 +176,7 @@ final class Utils {
|
||||
}
|
||||
|
||||
// It's possible to have no dimensions.
|
||||
this.dimensions = new HashMap();
|
||||
this.dimensions = new HashMap<>();
|
||||
while (rest != null) {
|
||||
final String nameValPair;
|
||||
if (rest.contains("&")) {
|
||||
@@ -216,11 +216,11 @@ final class Utils {
|
||||
}
|
||||
|
||||
static class SerieNameDecodeException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SerieNameDecodeException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 com.hpcloud.mon.infrastructure.persistence.mysql;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.skife.jdbi.v2.DBI;
|
||||
import org.skife.jdbi.v2.Handle;
|
||||
import org.skife.jdbi.v2.Query;
|
||||
import org.skife.jdbi.v2.util.StringMapper;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.hpcloud.mon.common.model.alarm.AggregateFunction;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmOperator;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmSubExpression;
|
||||
import com.hpcloud.mon.common.model.metric.MetricDefinition;
|
||||
import com.hpcloud.mon.domain.exception.EntityNotFoundException;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinition;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinitionRepository;
|
||||
import com.hpcloud.mon.infrastructure.persistence.DimensionQueries;
|
||||
import com.hpcloud.mon.infrastructure.persistence.SubAlarmQueries;
|
||||
import com.hpcloud.persistence.BeanMapper;
|
||||
|
||||
/**
|
||||
* Alarm repository implementation.
|
||||
*/
|
||||
public class AlarmDefinitionMySqlRepositoryImpl implements AlarmDefinitionRepository {
|
||||
private static final Joiner COMMA_JOINER = Joiner.on(',');
|
||||
private static final String SUB_ALARM_SQL =
|
||||
"select sa.*, sad.dimensions from sub_alarm_definition as sa "
|
||||
+ "left join (select sub_alarm_definition_id, group_concat(dimension_name, '=', value) as dimensions from sub_alarm_definition_dimension group by sub_alarm_definition_id ) as sad "
|
||||
+ "on sad.sub_alarm_definition_id = sa.id where sa.alarm_definition_id = :alarmDefId";
|
||||
|
||||
private final DBI db;
|
||||
|
||||
@Inject
|
||||
public AlarmDefinitionMySqlRepositoryImpl(@Named("mysql") DBI db) {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlarmDefinition create(String tenantId, String id, String name, String description,
|
||||
String severity, String expression, Map<String, AlarmSubExpression> subExpressions,
|
||||
List<String> matchBy, List<String> alarmActions, List<String> okActions,
|
||||
List<String> undeterminedActions) {
|
||||
Handle h = db.open();
|
||||
|
||||
try {
|
||||
h.begin();
|
||||
h.insert(
|
||||
"insert into alarm_definition (id, tenant_id, name, description, severity, expression, match_by, actions_enabled, created_at, updated_at, deleted_at) values (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW(), NULL)",
|
||||
id, tenantId, name, description, severity, expression,
|
||||
matchBy == null || Iterables.isEmpty(matchBy) ? null : COMMA_JOINER.join(matchBy), true);
|
||||
|
||||
// Persist sub-alarms
|
||||
createSubExpressions(h, id, subExpressions);
|
||||
|
||||
// Persist actions
|
||||
persistActions(h, id, AlarmState.ALARM, alarmActions);
|
||||
persistActions(h, id, AlarmState.OK, okActions);
|
||||
persistActions(h, id, AlarmState.UNDETERMINED, undeterminedActions);
|
||||
|
||||
h.commit();
|
||||
return new AlarmDefinition(id, name, description, severity, expression, matchBy, true,
|
||||
alarmActions, okActions == null ? Collections.<String>emptyList() : okActions,
|
||||
undeterminedActions == null ? Collections.<String>emptyList() : undeterminedActions);
|
||||
} catch (RuntimeException e) {
|
||||
h.rollback();
|
||||
throw e;
|
||||
} finally {
|
||||
h.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(String tenantId, String alarmDefId) {
|
||||
try (Handle h = db.open()) {
|
||||
if (h
|
||||
.update(
|
||||
"update alarm_definition set deleted_at = NOW() where tenant_id = ? and id = ? and deleted_at is NULL",
|
||||
tenantId, alarmDefId) == 0)
|
||||
throw new EntityNotFoundException("No alarm definition exists for %s", alarmDefId);
|
||||
|
||||
// Cascade soft delete to alarms
|
||||
h.execute("delete from alarm where alarm_definition_id = :id", alarmDefId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String tenantId, String name) {
|
||||
try (Handle h = db.open()) {
|
||||
return h
|
||||
.createQuery(
|
||||
"select exists(select 1 from alarm_definition where tenant_id = :tenantId and name = :name and deleted_at is NULL)")
|
||||
.bind("tenantId", tenantId).bind("name", name).mapTo(Boolean.TYPE).first();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<AlarmDefinition> find(String tenantId, String name, Map<String, String> dimensions) {
|
||||
try (Handle h = db.open()) {
|
||||
String query =
|
||||
"select distinct ad.id, ad.description, ad.tenant_id, ad.severity, ad.expression, ad.match_by, ad.name, ad.actions_enabled, ad.created_at, ad.updated_at, ad.deleted_at "
|
||||
+ "from alarm_definition ad join sub_alarm_definition sub on ad.id = sub.alarm_definition_id "
|
||||
+ "left outer join sub_alarm_definition_dimension dim on sub.id = dim.sub_alarm_definition_id%s "
|
||||
+ "where tenant_id = :tenantId and deleted_at is NULL %s order by ad.created_at";
|
||||
StringBuilder sbWhere = new StringBuilder();
|
||||
|
||||
if (name != null) {
|
||||
sbWhere.append(" and ad.name = :name");
|
||||
}
|
||||
|
||||
String sql = String.format(query, SubAlarmQueries.buildJoinClauseFor(dimensions), sbWhere);
|
||||
Query<?> q = h.createQuery(sql).bind("tenantId", tenantId);
|
||||
|
||||
if (name != null) {
|
||||
q.bind("name", name);
|
||||
}
|
||||
|
||||
q = q.map(new BeanMapper<AlarmDefinition>(AlarmDefinition.class));
|
||||
DimensionQueries.bindDimensionsToQuery(q, dimensions);
|
||||
|
||||
List<AlarmDefinition> alarms = (List<AlarmDefinition>) q.list();
|
||||
|
||||
for (AlarmDefinition alarm : alarms)
|
||||
hydrateRelationships(h, alarm);
|
||||
return alarms;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlarmDefinition findById(String tenantId, String alarmDefId) {
|
||||
try (Handle h = db.open()) {
|
||||
AlarmDefinition alarm =
|
||||
h.createQuery(
|
||||
"select * from alarm_definition where tenant_id = :tenantId and id = :id and deleted_at is NULL")
|
||||
.bind("tenantId", tenantId).bind("id", alarmDefId)
|
||||
.map(new BeanMapper<AlarmDefinition>(AlarmDefinition.class)).first();
|
||||
|
||||
if (alarm == null)
|
||||
throw new EntityNotFoundException("No alarm definition exists for %s", alarmDefId);
|
||||
|
||||
hydrateRelationships(h, alarm);
|
||||
return alarm;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, MetricDefinition> findSubAlarmMetricDefinitions(String alarmDefId) {
|
||||
try (Handle h = db.open()) {
|
||||
List<Map<String, Object>> rows =
|
||||
h.createQuery(SUB_ALARM_SQL).bind("alarmDefId", alarmDefId).list();
|
||||
Map<String, MetricDefinition> subAlarmMetricDefs = new HashMap<>();
|
||||
for (Map<String, Object> row : rows) {
|
||||
String id = (String) row.get("id");
|
||||
String metricName = (String) row.get("metric_name");
|
||||
Map<String, String> dimensions =
|
||||
DimensionQueries.dimensionsFor((String) row.get("dimensions"));
|
||||
subAlarmMetricDefs.put(id, new MetricDefinition(metricName, dimensions));
|
||||
}
|
||||
|
||||
return subAlarmMetricDefs;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, AlarmSubExpression> findSubExpressions(String alarmDefId) {
|
||||
try (Handle h = db.open()) {
|
||||
List<Map<String, Object>> rows =
|
||||
h.createQuery(SUB_ALARM_SQL).bind("alarmDefId", alarmDefId).list();
|
||||
Map<String, AlarmSubExpression> subExpressions = new HashMap<>();
|
||||
for (Map<String, Object> row : rows) {
|
||||
String id = (String) row.get("id");
|
||||
AggregateFunction function = AggregateFunction.fromJson((String) row.get("function"));
|
||||
String metricName = (String) row.get("metric_name");
|
||||
AlarmOperator operator = AlarmOperator.fromJson((String) row.get("operator"));
|
||||
Double threshold = (Double) row.get("threshold");
|
||||
Integer period = (Integer) row.get("period");
|
||||
Integer periods = (Integer) row.get("periods");
|
||||
Map<String, String> dimensions =
|
||||
DimensionQueries.dimensionsFor((String) row.get("dimensions"));
|
||||
subExpressions.put(id, new AlarmSubExpression(function, new MetricDefinition(metricName,
|
||||
dimensions), operator, threshold, period, periods));
|
||||
}
|
||||
|
||||
return subExpressions;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String tenantId, String id, boolean patch, String name, String description,
|
||||
String expression, List<String> matchBy, String severity, boolean actionsEnabled,
|
||||
Collection<String> oldSubAlarmIds, Map<String, AlarmSubExpression> changedSubAlarms,
|
||||
Map<String, AlarmSubExpression> newSubAlarms, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions) {
|
||||
Handle h = db.open();
|
||||
|
||||
try {
|
||||
h.begin();
|
||||
h.insert(
|
||||
"update alarm_definition set name = ?, description = ?, expression = ?, match_by = ?, severity = ?, actions_enabled = ?, updated_at = NOW() where tenant_id = ? and id = ?",
|
||||
name, description, expression, matchBy == null || Iterables.isEmpty(matchBy) ? null
|
||||
: COMMA_JOINER.join(matchBy), severity, actionsEnabled, tenantId, id);
|
||||
|
||||
// Delete old sub-alarms
|
||||
if (oldSubAlarmIds != null)
|
||||
for (String oldSubAlarmId : oldSubAlarmIds)
|
||||
h.execute("delete from sub_alarm_definition where id = ?", oldSubAlarmId);
|
||||
|
||||
// Update changed sub-alarms
|
||||
if (changedSubAlarms != null)
|
||||
for (Map.Entry<String, AlarmSubExpression> entry : changedSubAlarms.entrySet()) {
|
||||
AlarmSubExpression sa = entry.getValue();
|
||||
h.execute(
|
||||
"update sub_alarm_definition set operator = ?, threshold = ?, updated_at = NOW() where id = ?",
|
||||
sa.getOperator().name(), sa.getThreshold(), entry.getKey());
|
||||
}
|
||||
|
||||
// Insert new sub-alarms
|
||||
createSubExpressions(h, id, newSubAlarms);
|
||||
|
||||
// Delete old actions
|
||||
if (patch) {
|
||||
deleteActions(h, id, AlarmState.ALARM, alarmActions);
|
||||
deleteActions(h, id, AlarmState.OK, okActions);
|
||||
deleteActions(h, id, AlarmState.UNDETERMINED, undeterminedActions);
|
||||
} else
|
||||
h.execute("delete from alarm_action where alarm_id = ?", id);
|
||||
|
||||
// Insert new actions
|
||||
persistActions(h, id, AlarmState.ALARM, alarmActions);
|
||||
persistActions(h, id, AlarmState.OK, okActions);
|
||||
persistActions(h, id, AlarmState.UNDETERMINED, undeterminedActions);
|
||||
|
||||
h.commit();
|
||||
} catch (RuntimeException e) {
|
||||
h.rollback();
|
||||
throw e;
|
||||
} finally {
|
||||
h.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteActions(Handle handle, String id, AlarmState alarmState, List<String> actions) {
|
||||
if (actions != null)
|
||||
handle.execute("delete from alarm_action where alarm_id = ? and alarm_state = ?", id,
|
||||
alarmState.name());
|
||||
}
|
||||
|
||||
private List<String> findActionsById(Handle handle, String alarmDefId, AlarmState state) {
|
||||
return handle
|
||||
.createQuery(
|
||||
"select action_id from alarm_action where alarm_id = :alarmDefId and alarm_state = :alarmState")
|
||||
.bind("alarmDefId", alarmDefId).bind("alarmState", state.name()).map(StringMapper.FIRST)
|
||||
.list();
|
||||
}
|
||||
|
||||
private void persistActions(Handle handle, String id, AlarmState alarmState, List<String> actions) {
|
||||
if (actions != null)
|
||||
for (String action : actions)
|
||||
handle.insert("insert into alarm_action values (?, ?, ?)", id, alarmState.name(), action);
|
||||
}
|
||||
|
||||
private void hydrateRelationships(Handle handle, AlarmDefinition alarm) {
|
||||
alarm.setAlarmActions(findActionsById(handle, alarm.getId(), AlarmState.ALARM));
|
||||
alarm.setOkActions(findActionsById(handle, alarm.getId(), AlarmState.OK));
|
||||
alarm.setUndeterminedActions(findActionsById(handle, alarm.getId(), AlarmState.UNDETERMINED));
|
||||
}
|
||||
|
||||
private void createSubExpressions(Handle handle, String id,
|
||||
Map<String, AlarmSubExpression> alarmSubExpressions) {
|
||||
if (alarmSubExpressions != null) {
|
||||
for (Map.Entry<String, AlarmSubExpression> subEntry : alarmSubExpressions.entrySet()) {
|
||||
String subAlarmId = subEntry.getKey();
|
||||
AlarmSubExpression subExpr = subEntry.getValue();
|
||||
MetricDefinition metricDef = subExpr.getMetricDefinition();
|
||||
|
||||
// Persist sub-alarm
|
||||
handle
|
||||
.insert(
|
||||
"insert into sub_alarm_definition (id, alarm_definition_id, function, metric_name, operator, threshold, period, periods, created_at, updated_at) "
|
||||
+ "values (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())", subAlarmId, id, subExpr
|
||||
.getFunction().name(), metricDef.name, subExpr.getOperator().name(), subExpr
|
||||
.getThreshold(), subExpr.getPeriod(), subExpr.getPeriods());
|
||||
|
||||
// Persist sub-alarm dimensions
|
||||
if (metricDef.dimensions != null && !metricDef.dimensions.isEmpty())
|
||||
for (Map.Entry<String, String> dimEntry : metricDef.dimensions.entrySet())
|
||||
handle.insert("insert into sub_alarm_definition_dimension values (?, ?, ?)", subAlarmId,
|
||||
dimEntry.getKey(), dimEntry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,7 @@
|
||||
*/
|
||||
package com.hpcloud.mon.infrastructure.persistence.mysql;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -25,228 +23,150 @@ import javax.inject.Named;
|
||||
import org.skife.jdbi.v2.DBI;
|
||||
import org.skife.jdbi.v2.Handle;
|
||||
import org.skife.jdbi.v2.Query;
|
||||
import org.skife.jdbi.v2.util.StringMapper;
|
||||
|
||||
import com.hpcloud.mon.common.model.alarm.AggregateFunction;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmOperator;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmSubExpression;
|
||||
import com.hpcloud.mon.common.model.metric.MetricDefinition;
|
||||
import com.hpcloud.mon.domain.exception.EntityNotFoundException;
|
||||
import com.hpcloud.mon.domain.model.alarm.Alarm;
|
||||
import com.hpcloud.mon.domain.model.alarm.AlarmRepository;
|
||||
import com.hpcloud.mon.infrastructure.persistence.DimensionQueries;
|
||||
import com.hpcloud.mon.infrastructure.persistence.SubAlarmQueries;
|
||||
import com.hpcloud.persistence.BeanMapper;
|
||||
import com.hpcloud.persistence.SqlQueries;
|
||||
|
||||
/**
|
||||
* Alarm repository implementation.
|
||||
* Alarmed metric repository implementation.
|
||||
*/
|
||||
public class AlarmMySqlRepositoryImpl implements AlarmRepository {
|
||||
private static final String SUB_ALARM_SQL =
|
||||
"select sa.*, sad.dimensions from sub_alarm as sa "
|
||||
+ "left join (select sub_alarm_id, group_concat(dimension_name, '=', value) as dimensions from sub_alarm_dimension group by sub_alarm_id ) as sad "
|
||||
+ "on sad.sub_alarm_id = sa.id where sa.alarm_id = :alarmId";
|
||||
|
||||
private final DBI db;
|
||||
private static final String METRIC_DEFS_FOR_ALARM_SQL =
|
||||
"select md.name, mdg.dimensions from metric_definition as md "
|
||||
+ "inner join metric_definition_dimensions as mdd on mdd.metric_definition_id = md.id "
|
||||
+ "inner join alarm_metric as am on am.metric_definition_dimensions_id = mdd.id "
|
||||
+ "left join (select dimension_set_id, group_concat(name, '=', value) as dimensions from metric_dimension group by dimension_set_id) as mdg on mdg.dimension_set_id = mdd.metric_dimension_set_id "
|
||||
+ "where am.alarm_id = :alarmId";
|
||||
private static final String ALARM_SQL =
|
||||
"select distinct a.id, a.alarm_definition_id, a.state from alarm a "
|
||||
+ "inner join alarm_metric am on am.alarm_id = a.id "
|
||||
+ "inner join metric_definition_dimensions mdd on mdd.id = am.metric_definition_dimensions_id "
|
||||
+ "inner join metric_definition md on md.id = mdd.metric_definition_id%s "
|
||||
+ "inner join alarm_definition ad on ad.id = a.alarm_definition_id "
|
||||
+ "where ad.tenant_id = :tenantId%s order by a.created_at";
|
||||
|
||||
@Inject
|
||||
public AlarmMySqlRepositoryImpl(@Named("mysql") DBI db) {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
static String buildJoinClauseFor(Map<String, String> dimensions) {
|
||||
StringBuilder sbJoin = null;
|
||||
if (dimensions != null) {
|
||||
sbJoin = new StringBuilder();
|
||||
for (int i = 0; i < dimensions.size(); i++) {
|
||||
sbJoin.append(" inner join metric_dimension d").append(i).append(" on d").append(i)
|
||||
.append(".name = :dname").append(i).append(" and d").append(i)
|
||||
.append(".value = :dvalue").append(i).append(" and mdd.metric_dimension_set_id = d")
|
||||
.append(i).append(".dimension_set_id");
|
||||
}
|
||||
}
|
||||
|
||||
return sbJoin == null ? "" : sbJoin.toString();
|
||||
}
|
||||
|
||||
static Map<String, String> dimensionsFor(Handle handle, byte[] dimensionSetId) {
|
||||
return SqlQueries.keyValuesFor(handle, "select name, value from metric_dimension " + "where"
|
||||
+ " dimension_set_id = ?", dimensionSetId);
|
||||
}
|
||||
|
||||
private static List<MetricDefinition> findMetrics(Handle handle, String alarmId) {
|
||||
List<MetricDefinition> metricDefs = new ArrayList<>();
|
||||
for (Map<String, Object> row : handle.createQuery(METRIC_DEFS_FOR_ALARM_SQL)
|
||||
.bind("alarmId", alarmId).list()) {
|
||||
String metName = (String) row.get("name");
|
||||
Map<String, String> dimensions =
|
||||
DimensionQueries.dimensionsFor((String) row.get("dimensions"));
|
||||
metricDefs.add(new MetricDefinition(metName, dimensions));
|
||||
}
|
||||
|
||||
return metricDefs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alarm create(String tenantId, String id, String name, String description, String severity,
|
||||
String expression, Map<String, AlarmSubExpression> subExpressions, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions) {
|
||||
Handle h = db.open();
|
||||
|
||||
try {
|
||||
h.begin();
|
||||
h.insert(
|
||||
"insert into alarm (id, tenant_id, name, description, severity, expression, state, actions_enabled, created_at, updated_at, deleted_at) values (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW(), NULL)",
|
||||
id, tenantId, name, description, severity, expression,
|
||||
AlarmState.UNDETERMINED.toString(), true);
|
||||
|
||||
// Persist sub-alarms
|
||||
createSubExpressions(h, id, subExpressions);
|
||||
|
||||
// Persist actions
|
||||
persistActions(h, id, AlarmState.ALARM, alarmActions);
|
||||
persistActions(h, id, AlarmState.OK, okActions);
|
||||
persistActions(h, id, AlarmState.UNDETERMINED, undeterminedActions);
|
||||
|
||||
h.commit();
|
||||
return new Alarm(id, name, description, severity, expression, AlarmState.UNDETERMINED, true,
|
||||
alarmActions, okActions == null ? Collections.<String>emptyList() : okActions,
|
||||
undeterminedActions == null ? Collections.<String>emptyList() : undeterminedActions);
|
||||
} catch (RuntimeException e) {
|
||||
h.rollback();
|
||||
throw e;
|
||||
} finally {
|
||||
h.close();
|
||||
public void deleteById(String id) {
|
||||
try (Handle h = db.open()) {
|
||||
h.execute("delete from alarm where id = :id", id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(String tenantId, String alarmId) {
|
||||
public List<Alarm> find(String tenantId, String alarmDefId, String metricName,
|
||||
Map<String, String> metricDimensions, AlarmState state) {
|
||||
try (Handle h = db.open()) {
|
||||
if (h
|
||||
.update(
|
||||
"update alarm set deleted_at = NOW() where tenant_id = ? and id = ? and deleted_at is NULL",
|
||||
tenantId, alarmId) == 0)
|
||||
throw new EntityNotFoundException("No alarm exists for %s", alarmId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String tenantId, String name) {
|
||||
try (Handle h = db.open()) {
|
||||
return h
|
||||
.createQuery(
|
||||
"select exists(select 1 from alarm where tenant_id = :tenantId and name = :name and deleted_at is NULL)")
|
||||
.bind("tenantId", tenantId).bind("name", name).mapTo(Boolean.TYPE).first();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Alarm> find(String tenantId, String name, Map<String, String> dimensions, String state) {
|
||||
try (Handle h = db.open()) {
|
||||
String query =
|
||||
"select distinct alarm.id, alarm.description, alarm.tenant_id, alarm.severity, alarm.expression, alarm.state, alarm.name, alarm.actions_enabled, alarm.created_at, alarm.updated_at, alarm.deleted_at "
|
||||
+ "from alarm join sub_alarm sub on alarm.id = sub.alarm_id "
|
||||
+ "left outer join sub_alarm_dimension dim on sub.id = dim.sub_alarm_id%s "
|
||||
+ "where tenant_id = :tenantId and deleted_at is NULL %s";
|
||||
StringBuilder sbWhere = new StringBuilder();
|
||||
|
||||
if (name != null) {
|
||||
sbWhere.append(" and alarm.name = :name");
|
||||
if (alarmDefId != null) {
|
||||
sbWhere.append(" and ad.id = :alarmDefId");
|
||||
}
|
||||
if (metricName != null) {
|
||||
sbWhere.append(" and md.name = :metricName");
|
||||
}
|
||||
if (state != null) {
|
||||
sbWhere.append(" and alarm.state = :state");
|
||||
sbWhere.append(" and a.state = :state");
|
||||
}
|
||||
|
||||
String sql = String.format(query, SubAlarmQueries.buildJoinClauseFor(dimensions), sbWhere);
|
||||
String sql = String.format(ALARM_SQL, buildJoinClauseFor(metricDimensions), sbWhere);
|
||||
Query<?> q = h.createQuery(sql).bind("tenantId", tenantId);
|
||||
|
||||
if (name != null) {
|
||||
q.bind("name", name);
|
||||
if (alarmDefId != null) {
|
||||
q.bind("alarmDefId", alarmDefId);
|
||||
}
|
||||
if (metricName != null) {
|
||||
q.bind("metricName", metricName);
|
||||
}
|
||||
if (state != null) {
|
||||
q.bind("state", state);
|
||||
q.bind("state", state.name());
|
||||
}
|
||||
|
||||
q = q.map(new BeanMapper<Alarm>(Alarm.class));
|
||||
DimensionQueries.bindDimensionsToQuery(q, dimensions);
|
||||
DimensionQueries.bindDimensionsToQuery(q, metricDimensions);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Alarm> alarms = (List<Alarm>) q.list();
|
||||
|
||||
for (Alarm alarm : alarms)
|
||||
hydrateRelationships(h, alarm);
|
||||
for (Alarm alarm : alarms) {
|
||||
alarm.setMetrics(findMetrics(h, alarm.getId()));
|
||||
}
|
||||
return alarms;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alarm findById(String tenantId, String alarmId) {
|
||||
public Alarm findById(String alarmId) {
|
||||
try (Handle h = db.open()) {
|
||||
Alarm alarm =
|
||||
h.createQuery(
|
||||
"select * from alarm where tenant_id = :tenantId and id = :id and deleted_at is NULL")
|
||||
.bind("tenantId", tenantId).bind("id", alarmId)
|
||||
h.createQuery("select * from alarm where id = :id").bind("id", alarmId)
|
||||
.map(new BeanMapper<Alarm>(Alarm.class)).first();
|
||||
|
||||
if (alarm == null)
|
||||
throw new EntityNotFoundException("No alarm exists for %s", alarmId);
|
||||
|
||||
hydrateRelationships(h, alarm);
|
||||
// Hydrate metrics
|
||||
alarm.setMetrics(findMetrics(h, alarm.getId()));
|
||||
return alarm;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, MetricDefinition> findSubAlarmMetricDefinitions(String alarmId) {
|
||||
public List<MetricDefinition> findMetrics(String alarmId) {
|
||||
try (Handle h = db.open()) {
|
||||
List<Map<String, Object>> rows = h.createQuery(SUB_ALARM_SQL).bind("alarmId", alarmId).list();
|
||||
Map<String, MetricDefinition> subAlarmMetricDefs = new HashMap<>();
|
||||
for (Map<String, Object> row : rows) {
|
||||
String id = (String) row.get("id");
|
||||
String metricName = (String) row.get("metric_name");
|
||||
Map<String, String> dimensions = dimensionsFor((String) row.get("dimensions"));
|
||||
subAlarmMetricDefs.put(id, new MetricDefinition(metricName, dimensions));
|
||||
}
|
||||
|
||||
return subAlarmMetricDefs;
|
||||
return findMetrics(h, alarmId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, AlarmSubExpression> findSubExpressions(String alarmId) {
|
||||
try (Handle h = db.open()) {
|
||||
List<Map<String, Object>> rows = h.createQuery(SUB_ALARM_SQL).bind("alarmId", alarmId).list();
|
||||
Map<String, AlarmSubExpression> subExpressions = new HashMap<>();
|
||||
for (Map<String, Object> row : rows) {
|
||||
String id = (String) row.get("id");
|
||||
AggregateFunction function = AggregateFunction.fromJson((String) row.get("function"));
|
||||
String metricName = (String) row.get("metric_name");
|
||||
AlarmOperator operator = AlarmOperator.fromJson((String) row.get("operator"));
|
||||
Double threshold = (Double) row.get("threshold");
|
||||
Integer period = (Integer) row.get("period");
|
||||
Integer periods = (Integer) row.get("periods");
|
||||
Map<String, String> dimensions = dimensionsFor((String) row.get("dimensions"));
|
||||
subExpressions.put(id, new AlarmSubExpression(function, new MetricDefinition(metricName,
|
||||
dimensions), operator, threshold, period, periods));
|
||||
}
|
||||
|
||||
return subExpressions;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String tenantId, String id, boolean patch, String name, String description,
|
||||
String expression, String severity, AlarmState state, boolean actionsEnabled,
|
||||
Collection<String> oldSubAlarmIds, Map<String, AlarmSubExpression> changedSubAlarms,
|
||||
Map<String, AlarmSubExpression> newSubAlarms, List<String> alarmActions,
|
||||
List<String> okActions, List<String> undeterminedActions) {
|
||||
public void update(String tenantId, String id, AlarmState state) {
|
||||
Handle h = db.open();
|
||||
|
||||
try {
|
||||
h.begin();
|
||||
h.insert(
|
||||
"update alarm set name = ?, description = ?, expression = ?, severity = ?, state = ?, actions_enabled = ?, updated_at = NOW() where tenant_id = ? and id = ?",
|
||||
name, description, expression, severity, state.name(), actionsEnabled, tenantId, id);
|
||||
|
||||
// Delete old sub-alarms
|
||||
if (oldSubAlarmIds != null)
|
||||
for (String oldSubAlarmId : oldSubAlarmIds)
|
||||
h.execute("delete from sub_alarm where id = ?", oldSubAlarmId);
|
||||
|
||||
// Update changed sub-alarms
|
||||
if (changedSubAlarms != null)
|
||||
for (Map.Entry<String, AlarmSubExpression> entry : changedSubAlarms.entrySet()) {
|
||||
AlarmSubExpression sa = entry.getValue();
|
||||
h.execute(
|
||||
"update sub_alarm set operator = ?, threshold = ?, updated_at = NOW() where id = ?",
|
||||
sa.getOperator().name(), sa.getThreshold(), entry.getKey());
|
||||
}
|
||||
|
||||
// Insert new sub-alarms
|
||||
createSubExpressions(h, id, newSubAlarms);
|
||||
|
||||
// Delete old actions
|
||||
if (patch) {
|
||||
deleteActions(h, id, AlarmState.ALARM, alarmActions);
|
||||
deleteActions(h, id, AlarmState.OK, okActions);
|
||||
deleteActions(h, id, AlarmState.UNDETERMINED, undeterminedActions);
|
||||
} else
|
||||
h.execute("delete from alarm_action where alarm_id = ?", id);
|
||||
|
||||
// Insert new actions
|
||||
persistActions(h, id, AlarmState.ALARM, alarmActions);
|
||||
persistActions(h, id, AlarmState.OK, okActions);
|
||||
persistActions(h, id, AlarmState.UNDETERMINED, undeterminedActions);
|
||||
|
||||
h.insert("update alarm set state = ?, updated_at = NOW() where id = ?", state.name(), id);
|
||||
h.commit();
|
||||
} catch (RuntimeException e) {
|
||||
h.rollback();
|
||||
@@ -255,69 +175,4 @@ public class AlarmMySqlRepositoryImpl implements AlarmRepository {
|
||||
h.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteActions(Handle handle, String id, AlarmState alarmState, List<String> actions) {
|
||||
if (actions != null)
|
||||
handle.execute("delete from alarm_action where alarm_id = ? and alarm_state = ?", id,
|
||||
alarmState.name());
|
||||
}
|
||||
|
||||
private Map<String, String> dimensionsFor(String dimensionSet) {
|
||||
Map<String, String> dimensions = null;
|
||||
|
||||
if (dimensionSet != null) {
|
||||
dimensions = new HashMap<String, String>();
|
||||
for (String kvStr : dimensionSet.split(",")) {
|
||||
String[] kv = kvStr.split("=");
|
||||
if (kv.length > 1)
|
||||
dimensions.put(kv[0], kv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
private List<String> findActionsById(Handle handle, String alarmId, AlarmState state) {
|
||||
return handle
|
||||
.createQuery(
|
||||
"select action_id from alarm_action where alarm_id = :alarmId and alarm_state = :alarmState")
|
||||
.bind("alarmId", alarmId).bind("alarmState", state.name()).map(StringMapper.FIRST).list();
|
||||
}
|
||||
|
||||
private void persistActions(Handle handle, String id, AlarmState alarmState, List<String> actions) {
|
||||
if (actions != null)
|
||||
for (String action : actions)
|
||||
handle.insert("insert into alarm_action values (?, ?, ?)", id, alarmState.name(), action);
|
||||
}
|
||||
|
||||
private void hydrateRelationships(Handle handle, Alarm alarm) {
|
||||
alarm.setAlarmActions(findActionsById(handle, alarm.getId(), AlarmState.ALARM));
|
||||
alarm.setOkActions(findActionsById(handle, alarm.getId(), AlarmState.OK));
|
||||
alarm.setUndeterminedActions(findActionsById(handle, alarm.getId(), AlarmState.UNDETERMINED));
|
||||
}
|
||||
|
||||
private void createSubExpressions(Handle handle, String id,
|
||||
Map<String, AlarmSubExpression> alarmSubExpressions) {
|
||||
if (alarmSubExpressions != null)
|
||||
for (Map.Entry<String, AlarmSubExpression> subEntry : alarmSubExpressions.entrySet()) {
|
||||
String subAlarmId = subEntry.getKey();
|
||||
AlarmSubExpression subExpr = subEntry.getValue();
|
||||
MetricDefinition metricDef = subExpr.getMetricDefinition();
|
||||
|
||||
// Persist sub-alarm
|
||||
handle
|
||||
.insert(
|
||||
"insert into sub_alarm (id, alarm_id, function, metric_name, operator, threshold, period, periods, state, created_at, updated_at) "
|
||||
+ "values (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())", subAlarmId, id, subExpr
|
||||
.getFunction().name(), metricDef.name, subExpr.getOperator().name(), subExpr
|
||||
.getThreshold(), subExpr.getPeriod(), subExpr.getPeriods(),
|
||||
AlarmState.UNDETERMINED.toString());
|
||||
|
||||
// Persist sub-alarm dimensions
|
||||
if (metricDef.dimensions != null && !metricDef.dimensions.isEmpty())
|
||||
for (Map.Entry<String, String> dimEntry : metricDef.dimensions.entrySet())
|
||||
handle.insert("insert into sub_alarm_dimension values (?, ?, ?)", subAlarmId,
|
||||
dimEntry.getKey(), dimEntry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +43,11 @@ import com.hpcloud.persistence.BeanMapper;
|
||||
public class AlarmStateHistoryVerticaRepositoryImpl implements AlarmStateHistoryRepository {
|
||||
public static final DateTimeFormatter DATETIME_FORMATTER = ISODateTimeFormat.dateTimeNoMillis()
|
||||
.withZoneUTC();
|
||||
private static final String FIND_ALARMS_SQL = "select distinct a.id from alarm as a "
|
||||
+ "join sub_alarm sa on a.id = sa.alarm_id "
|
||||
+ "left outer join sub_alarm_dimension dim on sa.id = dim.sub_alarm_id%s "
|
||||
+ "where a.tenant_id = :tenantId and a.deleted_at is NULL";
|
||||
private static final String FIND_ALARMS_SQL =
|
||||
"select distinct ad.id from alarm_definition as ad "
|
||||
+ "join sub_alarm_definition sad on ad.id = sad.alarm_definition_id "
|
||||
+ "left outer join sub_alarm_definition_dimension dim on sad.id = dim.sub_alarm_definition_id%s "
|
||||
+ "where ad.tenant_id = :tenantId and ad.deleted_at is NULL";
|
||||
private static final String FIND_BY_ALARM_DEF_SQL =
|
||||
"select *, time_stamp as timestamp from MonAlarms.StateHistory "
|
||||
+ "where tenant_id = :tenantId%s order by time_stamp desc";
|
||||
|
||||
@@ -15,7 +15,6 @@ package com.hpcloud.mon.infrastructure.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 com.hpcloud.mon.resource;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.validation.Valid;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.google.common.base.Strings;
|
||||
import com.hpcloud.mon.app.AlarmDefinitionService;
|
||||
import com.hpcloud.mon.app.command.CreateAlarmDefinitionCommand;
|
||||
import com.hpcloud.mon.app.command.UpdateAlarmDefinitionCommand;
|
||||
import com.hpcloud.mon.app.validation.AlarmValidation;
|
||||
import com.hpcloud.mon.app.validation.Validation;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmExpression;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinition;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinitionRepository;
|
||||
import com.hpcloud.mon.resource.annotation.PATCH;
|
||||
import com.wordnik.swagger.annotations.Api;
|
||||
import com.wordnik.swagger.annotations.ApiOperation;
|
||||
import com.wordnik.swagger.annotations.ApiParam;
|
||||
import com.wordnik.swagger.annotations.ApiResponse;
|
||||
import com.wordnik.swagger.annotations.ApiResponses;
|
||||
|
||||
/**
|
||||
* Alarm definition resource implementation.
|
||||
*/
|
||||
@Path("/v2.0/alarm-definitions")
|
||||
@Api(value = "/v2.0/alarm-definitions",
|
||||
description = "Operations for working with alarm definitions")
|
||||
public class AlarmDefinitionResource {
|
||||
private final AlarmDefinitionService service;
|
||||
private final AlarmDefinitionRepository repo;
|
||||
|
||||
@Inject
|
||||
public AlarmDefinitionResource(AlarmDefinitionService service, AlarmDefinitionRepository repo) {
|
||||
this.service = service;
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Create alarm definition", response = AlarmDefinition.class)
|
||||
public Response create(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@Valid CreateAlarmDefinitionCommand command) {
|
||||
command.validate();
|
||||
AlarmExpression alarmExpression = AlarmValidation.validateNormalizeAndGet(command.expression);
|
||||
AlarmDefinition alarm =
|
||||
Links.hydrate(service.create(tenantId, command.name, command.description, command.severity,
|
||||
command.expression, alarmExpression, command.matchBy, command.alarmActions,
|
||||
command.okActions, command.undeterminedActions), uriInfo, false);
|
||||
return Response.created(URI.create(alarm.getId())).entity(alarm).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "List alarm definitions", response = AlarmDefinition.class,
|
||||
responseContainer = "List")
|
||||
public List<AlarmDefinition> list(@Context UriInfo uriInfo,
|
||||
@HeaderParam("X-Tenant-Id") String tenantId, @QueryParam("name") String name,
|
||||
@QueryParam("dimensions") String dimensionsStr) {
|
||||
Map<String, String> dimensions =
|
||||
Strings.isNullOrEmpty(dimensionsStr) ? null : Validation
|
||||
.parseAndValidateDimensions(dimensionsStr);
|
||||
return Links.hydrate(repo.find(tenantId, name, dimensions), uriInfo);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("/{alarm_definition_id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Get alarm definition", response = AlarmDefinition.class)
|
||||
@ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid ID supplied"),
|
||||
@ApiResponse(code = 404, message = "Alarm definition not found")})
|
||||
public AlarmDefinition get(
|
||||
@ApiParam(value = "ID of alarm definition to fetch", required = true) @Context UriInfo uriInfo,
|
||||
@HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@PathParam("alarm_definition_id") String alarmDefinitionId) {
|
||||
return Links.hydrate(repo.findById(tenantId, alarmDefinitionId), uriInfo, true);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("/{alarm_definition_id}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Update alarm definition", response = AlarmDefinition.class)
|
||||
public AlarmDefinition update(@Context UriInfo uriInfo,
|
||||
@HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@PathParam("alarm_definition_id") String alarmDefinitionId,
|
||||
@Valid UpdateAlarmDefinitionCommand command) {
|
||||
command.validate();
|
||||
AlarmExpression alarmExpression = AlarmValidation.validateNormalizeAndGet(command.expression);
|
||||
return Links.hydrate(service.update(tenantId, alarmDefinitionId, alarmExpression, command),
|
||||
uriInfo, true);
|
||||
}
|
||||
|
||||
@PATCH
|
||||
@Timed
|
||||
@Path("/{alarm_definition_id}")
|
||||
@Consumes("application/json-patch+json")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@SuppressWarnings("unchecked")
|
||||
public AlarmDefinition patch(@Context UriInfo uriInfo,
|
||||
@HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@PathParam("alarm_definition_id") String alarmDefinitionId,
|
||||
@NotEmpty Map<String, Object> fields) throws JsonMappingException {
|
||||
String name = (String) fields.get("name");
|
||||
String description = (String) fields.get("description");
|
||||
String severity = (String) fields.get("severity");
|
||||
String expression = (String) fields.get("expression");
|
||||
List<String> matchBy = (List<String>) fields.get("match_by");
|
||||
String stateStr = (String) fields.get("state");
|
||||
AlarmState state =
|
||||
stateStr == null ? null : Validation.parseAndValidate(AlarmState.class, stateStr);
|
||||
Boolean enabled = (Boolean) fields.get("actions_enabled");
|
||||
List<String> alarmActions = (List<String>) fields.get("alarm_actions");
|
||||
List<String> okActions = (List<String>) fields.get("ok_actions");
|
||||
List<String> undeterminedActions = (List<String>) fields.get("undetermined_actions");
|
||||
AlarmValidation.validate(name, description, severity, alarmActions, okActions,
|
||||
undeterminedActions);
|
||||
AlarmExpression alarmExpression =
|
||||
expression == null ? null : AlarmValidation.validateNormalizeAndGet(expression);
|
||||
|
||||
return Links.hydrate(service.patch(tenantId, alarmDefinitionId, name, description, severity,
|
||||
expression, alarmExpression, matchBy, state, enabled, alarmActions, okActions,
|
||||
undeterminedActions), uriInfo, true);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("/{alarm_definition_id}")
|
||||
@ApiOperation(value = "Delete alarm definition")
|
||||
public void delete(@HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@PathParam("alarm_definition_id") String alarmDefinitionId) {
|
||||
service.delete(tenantId, alarmDefinitionId);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
*/
|
||||
package com.hpcloud.mon.resource;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -24,7 +23,6 @@ import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
@@ -32,7 +30,6 @@ import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
@@ -42,14 +39,12 @@ import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.google.common.base.Strings;
|
||||
import com.hpcloud.mon.app.AlarmService;
|
||||
import com.hpcloud.mon.app.command.CreateAlarmCommand;
|
||||
import com.hpcloud.mon.app.command.UpdateAlarmCommand;
|
||||
import com.hpcloud.mon.app.validation.AlarmValidation;
|
||||
import com.hpcloud.mon.app.validation.Validation;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmExpression;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.domain.model.alarm.Alarm;
|
||||
import com.hpcloud.mon.domain.model.alarm.AlarmRepository;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinition;
|
||||
import com.hpcloud.mon.domain.model.alarmstatehistory.AlarmStateHistory;
|
||||
import com.hpcloud.mon.domain.model.alarmstatehistory.AlarmStateHistoryRepository;
|
||||
import com.hpcloud.mon.resource.annotation.PATCH;
|
||||
@@ -63,7 +58,7 @@ import com.wordnik.swagger.annotations.ApiResponses;
|
||||
* Alarm resource implementation.
|
||||
*/
|
||||
@Path("/v2.0/alarms")
|
||||
@Api(value = "/v2.0/alarms", description = "Operations for working with alarms")
|
||||
@Api(value = "/v2.0/alarms", description = "Operations for accessing alarms")
|
||||
public class AlarmResource {
|
||||
private final AlarmService service;
|
||||
private final AlarmRepository repo;
|
||||
@@ -77,20 +72,38 @@ public class AlarmResource {
|
||||
this.stateHistoryRepo = stateHistoryRepo;
|
||||
}
|
||||
|
||||
@POST
|
||||
@DELETE
|
||||
@Timed
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Path("/{alarm_id}")
|
||||
@ApiOperation(value = "Delete alarm")
|
||||
public void delete(@HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@PathParam("alarm_id") String alarmId) {
|
||||
service.delete(tenantId, alarmId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("/{alarm_id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Create alarm", response = Alarm.class)
|
||||
public Response create(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@Valid CreateAlarmCommand command) {
|
||||
command.validate();
|
||||
AlarmExpression alarmExpression = AlarmValidation.validateNormalizeAndGet(command.expression);
|
||||
Alarm alarm =
|
||||
Links.hydrate(service.create(tenantId, command.name, command.description, command.severity,
|
||||
command.expression, alarmExpression, command.alarmActions, command.okActions,
|
||||
command.undeterminedActions), uriInfo, false, "history");
|
||||
return Response.created(URI.create(alarm.getId())).entity(alarm).build();
|
||||
@ApiOperation(value = "Get alarm", response = Alarm.class)
|
||||
@ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid ID supplied"),
|
||||
@ApiResponse(code = 404, message = "Alarm not found")})
|
||||
public Alarm get(
|
||||
@ApiParam(value = "ID of alarm to fetch", required = true) @Context UriInfo uriInfo,
|
||||
@HeaderParam("X-Tenant-Id") String tenantId, @PathParam("alarm_id") String alarm_id) {
|
||||
return Links.hydrate(repo.findById(alarm_id), uriInfo, true);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("/{alarm_id}/state-history")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Get alarm state history", response = AlarmStateHistory.class,
|
||||
responseContainer = "List")
|
||||
public List<AlarmStateHistory> getStateHistory(@Context UriInfo uriInfo,
|
||||
@HeaderParam("X-Tenant-Id") String tenantId, @PathParam("alarm_id") String alarmId)
|
||||
throws Exception {
|
||||
return stateHistoryRepo.findById(tenantId, alarmId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@@ -98,23 +111,22 @@ public class AlarmResource {
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "List alarms", response = Alarm.class, responseContainer = "List")
|
||||
public List<Alarm> list(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@QueryParam("name") String name, @QueryParam("dimensions") String dimensionsStr,
|
||||
@QueryParam("state") String state) {
|
||||
|
||||
Map<String, String> dimensions =
|
||||
Strings.isNullOrEmpty(dimensionsStr) ? null : Validation
|
||||
.parseAndValidateDimensions(dimensionsStr);
|
||||
if (state != null) {
|
||||
Validation.validateAlarmState(state);
|
||||
}
|
||||
return Links.hydrate(repo.find(tenantId, name, dimensions, state), uriInfo, "history");
|
||||
@QueryParam("alarm_definition_id") String alarmDefId,
|
||||
@QueryParam("metric_name") String metricName,
|
||||
@QueryParam("metric_dimensions") String metricDimensionsStr,
|
||||
@QueryParam("state") AlarmState state) throws Exception {
|
||||
Map<String, String> metricDimensions =
|
||||
Strings.isNullOrEmpty(metricDimensionsStr) ? null : Validation
|
||||
.parseAndValidateNameAndDimensions(metricName, metricDimensionsStr);
|
||||
return Links.hydrate(repo.find(tenantId, alarmDefId, metricName, metricDimensions, state),
|
||||
uriInfo);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("/state-history")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "List alarm state history", response = Alarm.class,
|
||||
@ApiOperation(value = "List alarm state history", response = AlarmDefinition.class,
|
||||
responseContainer = "List")
|
||||
public Collection<AlarmStateHistory> listStateHistory(
|
||||
@HeaderParam("X-Tenant-Id") String tenantId, @QueryParam("dimensions") String dimensionsStr,
|
||||
@@ -133,17 +145,19 @@ public class AlarmResource {
|
||||
return stateHistoryRepo.find(tenantId, dimensions, startTime, endTime);
|
||||
}
|
||||
|
||||
@GET
|
||||
@PATCH
|
||||
@Timed
|
||||
@Path("/{alarm_id}")
|
||||
@Consumes("application/json-patch+json")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Get alarm", response = Alarm.class)
|
||||
@ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid ID supplied"),
|
||||
@ApiResponse(code = 404, message = "Alarm not found")})
|
||||
public Alarm get(
|
||||
@ApiParam(value = "ID of alarm to fetch", required = true) @Context UriInfo uriInfo,
|
||||
@HeaderParam("X-Tenant-Id") String tenantId, @PathParam("alarm_id") String alarmId) {
|
||||
return Links.hydrate(repo.findById(tenantId, alarmId), uriInfo, true, "history");
|
||||
public Alarm patch(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@PathParam("alarm_id") String alarmId, @NotEmpty Map<String, Object> fields)
|
||||
throws JsonMappingException {
|
||||
String stateStr = (String) fields.get("state");
|
||||
AlarmState state =
|
||||
stateStr == null ? null : Validation.parseAndValidate(AlarmState.class, stateStr);
|
||||
|
||||
return Links.hydrate((Alarm) service.patch(tenantId, alarmId, state), uriInfo, true);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@@ -154,60 +168,6 @@ public class AlarmResource {
|
||||
@ApiOperation(value = "Update alarm", response = Alarm.class)
|
||||
public Alarm update(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@PathParam("alarm_id") String alarmId, @Valid UpdateAlarmCommand command) {
|
||||
command.validate();
|
||||
AlarmExpression alarmExpression = AlarmValidation.validateNormalizeAndGet(command.expression);
|
||||
return Links.hydrate(service.update(tenantId, alarmId, alarmExpression, command), uriInfo,
|
||||
true, "history");
|
||||
}
|
||||
|
||||
@PATCH
|
||||
@Timed
|
||||
@Path("/{alarm_id}")
|
||||
@Consumes("application/json-patch+json")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@SuppressWarnings("unchecked")
|
||||
public Alarm patch(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@PathParam("alarm_id") String alarmId, @NotEmpty Map<String, Object> fields)
|
||||
throws JsonMappingException {
|
||||
String name = (String) fields.get("name");
|
||||
String description = (String) fields.get("description");
|
||||
String severity = (String) fields.get("severity");
|
||||
String expression = (String) fields.get("expression");
|
||||
String stateStr = (String) fields.get("state");
|
||||
AlarmState state =
|
||||
stateStr == null ? null : Validation.parseAndValidate(AlarmState.class, stateStr);
|
||||
Boolean enabled = (Boolean) fields.get("actions_enabled");
|
||||
List<String> alarmActions = (List<String>) fields.get("alarm_actions");
|
||||
List<String> okActions = (List<String>) fields.get("ok_actions");
|
||||
List<String> undeterminedActions = (List<String>) fields.get("undetermined_actions");
|
||||
AlarmValidation.validate(name, description, severity, alarmActions, okActions,
|
||||
undeterminedActions);
|
||||
AlarmExpression alarmExpression =
|
||||
expression == null ? null : AlarmValidation.validateNormalizeAndGet(expression);
|
||||
|
||||
return Links.hydrate(service.patch(tenantId, alarmId, name, description, severity, expression,
|
||||
alarmExpression, state, enabled, alarmActions, okActions, undeterminedActions), uriInfo,
|
||||
true, "history");
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("/{alarm_id}")
|
||||
@ApiOperation(value = "Delete alarm")
|
||||
public void delete(@HeaderParam("X-Tenant-Id") String tenantId,
|
||||
@PathParam("alarm_id") String alarmId) {
|
||||
service.delete(tenantId, alarmId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("/{alarm_id}/state-history")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApiOperation(value = "Get alarm state history", response = AlarmStateHistory.class,
|
||||
responseContainer = "List")
|
||||
public List<AlarmStateHistory> getStateHistory(@Context UriInfo uriInfo,
|
||||
@HeaderParam("X-Tenant-Id") String tenantId, @PathParam("alarm_id") String alarmId)
|
||||
throws Exception {
|
||||
return stateHistoryRepo.findById(tenantId, alarmId);
|
||||
return Links.hydrate(service.update(tenantId, alarmId, command), uriInfo, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,17 @@ public final class Links {
|
||||
return resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates the {@code resource} with links for the {@code uriInfo}.
|
||||
*
|
||||
* @param resource to obtain id from
|
||||
* @param uriInfo to obtain path from
|
||||
* @throws NullPointerException if {@code resource} is null
|
||||
*/
|
||||
public static <T extends AbstractEntity & Linked> T hydrate(T resource, UriInfo uriInfo) {
|
||||
return hydrate(resource, prefixForHttps(uriInfo.getAbsolutePath().toString()), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates the {@code resource} with links for the {@code uriInfo}.
|
||||
*
|
||||
|
||||
@@ -18,7 +18,6 @@ import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
@@ -39,21 +38,20 @@ import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.hpcloud.mon.MonApiConfiguration;
|
||||
import com.hpcloud.mon.app.AlarmService.SubExpressions;
|
||||
import com.hpcloud.mon.app.command.UpdateAlarmCommand;
|
||||
import com.hpcloud.mon.app.AlarmDefinitionService.SubExpressions;
|
||||
import com.hpcloud.mon.app.command.UpdateAlarmDefinitionCommand;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmExpression;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmSubExpression;
|
||||
import com.hpcloud.mon.domain.model.alarm.Alarm;
|
||||
import com.hpcloud.mon.domain.model.alarm.AlarmRepository;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinition;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinitionRepository;
|
||||
import com.hpcloud.mon.domain.model.notificationmethod.NotificationMethodRepository;
|
||||
|
||||
@Test
|
||||
public class AlarmServiceTest {
|
||||
AlarmService service;
|
||||
public class AlarmDefinitionServiceTest {
|
||||
AlarmDefinitionService service;
|
||||
MonApiConfiguration config;
|
||||
Producer<String, String> producer;
|
||||
AlarmRepository repo;
|
||||
AlarmDefinitionRepository repo;
|
||||
NotificationMethodRepository notificationMethodRepo;
|
||||
|
||||
@BeforeMethod
|
||||
@@ -61,20 +59,20 @@ public class AlarmServiceTest {
|
||||
protected void beforeMethod() {
|
||||
config = new MonApiConfiguration();
|
||||
producer = mock(Producer.class);
|
||||
repo = mock(AlarmRepository.class);
|
||||
repo = mock(AlarmDefinitionRepository.class);
|
||||
notificationMethodRepo = mock(NotificationMethodRepository.class);
|
||||
service = new AlarmService(config, producer, repo, notificationMethodRepo);
|
||||
service = new AlarmDefinitionService(config, producer, repo, notificationMethodRepo);
|
||||
|
||||
when(
|
||||
repo.create(anyString(), anyString(), anyString(), anyString(), anyString(), anyString(),
|
||||
any(Map.class), any(List.class), any(List.class), any(List.class))).thenAnswer(
|
||||
new Answer<Alarm>() {
|
||||
any(Map.class), any(List.class), any(List.class), any(List.class), any(List.class)))
|
||||
.thenAnswer(new Answer<AlarmDefinition>() {
|
||||
@Override
|
||||
public Alarm answer(InvocationOnMock invocation) throws Throwable {
|
||||
public AlarmDefinition answer(InvocationOnMock invocation) throws Throwable {
|
||||
Object[] args = invocation.getArguments();
|
||||
return new Alarm((String) args[0], (String) args[2], (String) args[3],
|
||||
(String) args[4], (String) args[5], AlarmState.UNDETERMINED, true,
|
||||
(List<String>) args[7], (List<String>) args[8], (List<String>) args[9]);
|
||||
return new AlarmDefinition((String) args[0], (String) args[2], (String) args[3],
|
||||
(String) args[4], (String) args[5], (List<String>) args[7], true,
|
||||
(List<String>) args[8], (List<String>) args[9], (List<String>) args[10]);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -82,22 +80,23 @@ public class AlarmServiceTest {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreate() {
|
||||
String exprStr = "avg(cpu_utilization{service=hpcs.compute, instance_id=123}) > 90";
|
||||
List<String> matchBy = Arrays.asList("service", "instance_id");
|
||||
List<String> alarmActions = Arrays.asList("1", "2", "3");
|
||||
List<String> okActions = Arrays.asList("2", "3");
|
||||
List<String> undeterminedActions = Arrays.asList("3");
|
||||
|
||||
when(notificationMethodRepo.exists(eq("bob"), anyString())).thenReturn(true);
|
||||
|
||||
Alarm alarm =
|
||||
AlarmDefinition alarm =
|
||||
service.create("bob", "90% CPU", "foo", "LOW", exprStr, AlarmExpression.of(exprStr),
|
||||
alarmActions, okActions, undeterminedActions);
|
||||
matchBy, alarmActions, okActions, undeterminedActions);
|
||||
|
||||
Alarm expected =
|
||||
new Alarm(alarm.getId(), "90% CPU", "foo", "LOW", exprStr, AlarmState.UNDETERMINED, true,
|
||||
AlarmDefinition expected =
|
||||
new AlarmDefinition(alarm.getId(), "90% CPU", "foo", "LOW", exprStr, matchBy, true,
|
||||
alarmActions, okActions, undeterminedActions);
|
||||
assertEquals(expected, alarm);
|
||||
verify(repo).create(eq("bob"), anyString(), eq("90% CPU"), eq("foo"), eq("LOW"), eq(exprStr),
|
||||
any(Map.class), eq(alarmActions), eq(okActions), eq(undeterminedActions));
|
||||
any(Map.class), eq(matchBy), eq(alarmActions), eq(okActions), eq(undeterminedActions));
|
||||
verify(producer).send(any(KeyedMessage.class));
|
||||
}
|
||||
|
||||
@@ -108,8 +107,8 @@ public class AlarmServiceTest {
|
||||
List<String> okActions = Arrays.asList("2", "3");
|
||||
List<String> undeterminedActions = Arrays.asList("3");
|
||||
|
||||
Alarm oldAlarm =
|
||||
new Alarm("123", "foo bar", "foo bar", "LOW", exprStr, AlarmState.OK, true, alarmActions,
|
||||
AlarmDefinition oldAlarm =
|
||||
new AlarmDefinition("123", "foo bar", "foo bar", "LOW", exprStr, null, true, alarmActions,
|
||||
okActions, undeterminedActions);
|
||||
Map<String, AlarmSubExpression> oldSubExpressions = new HashMap<>();
|
||||
oldSubExpressions.put("444", AlarmSubExpression.of("avg(foo{instance_id=123}) > 90"));
|
||||
@@ -124,17 +123,17 @@ public class AlarmServiceTest {
|
||||
List<String> newAlarmActions = Arrays.asList("5", "6", "7");
|
||||
List<String> newOkActions = Arrays.asList("6", "7");
|
||||
List<String> newUndeterminedActions = Arrays.asList("7");
|
||||
UpdateAlarmCommand command =
|
||||
new UpdateAlarmCommand("foo bar baz", "foo bar baz", newExprStr, "LOW", AlarmState.ALARM,
|
||||
UpdateAlarmDefinitionCommand command =
|
||||
new UpdateAlarmDefinitionCommand("foo bar baz", "foo bar baz", newExprStr, null, "LOW",
|
||||
false, newAlarmActions, newOkActions, newUndeterminedActions);
|
||||
|
||||
Alarm alarm = service.update("bob", "123", AlarmExpression.of(newExprStr), command);
|
||||
AlarmDefinition alarm = service.update("bob", "123", AlarmExpression.of(newExprStr), command);
|
||||
|
||||
Alarm expected =
|
||||
new Alarm(alarm.getId(), "foo bar baz", "foo bar baz", "LOW", newExprStr, AlarmState.ALARM,
|
||||
AlarmDefinition expected =
|
||||
new AlarmDefinition(alarm.getId(), "foo bar baz", "foo bar baz", "LOW", newExprStr, null,
|
||||
false, newAlarmActions, newOkActions, newUndeterminedActions);
|
||||
assertEquals(expected, alarm);
|
||||
verify(producer, times(2)).send(any(KeyedMessage.class));
|
||||
verify(producer).send(any(KeyedMessage.class));
|
||||
}
|
||||
|
||||
public void testOldAndNewSubExpressionsFor() {
|
||||
@@ -23,22 +23,21 @@ import java.util.Map;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.hpcloud.mon.app.command.CreateAlarmCommand;
|
||||
import com.hpcloud.mon.domain.model.AbstractModelTest;
|
||||
|
||||
@Test
|
||||
public class CreateAlarmCommandTest extends AbstractModelTest {
|
||||
public class CreateAlarmDefinitionCommandTest extends AbstractModelTest {
|
||||
public void shouldDeserializeFromJson() throws Exception {
|
||||
Map<String, String> dimensions = new HashMap<String, String>();
|
||||
dimensions.put("instanceId", "392633");
|
||||
/** todo: Check the null value to get works **/
|
||||
CreateAlarmCommand newAlarm =
|
||||
new CreateAlarmCommand("Disk Exceeds 1k Operations", null, null,
|
||||
"avg(hpcs.compute:cpu:1:{instance_id=5}) > 5", Arrays.asList("123345345", "23423"),
|
||||
null, null);
|
||||
CreateAlarmDefinitionCommand newAlarm =
|
||||
new CreateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null,
|
||||
"avg(hpcs.compute:cpu:1:{instance_id=5}) > 5", null, null, Arrays.asList("123345345",
|
||||
"23423"), null, null);
|
||||
|
||||
String json = jsonFixture("fixtures/newAlarm.json");
|
||||
CreateAlarmCommand alarm = fromJson(json, CreateAlarmCommand.class);
|
||||
CreateAlarmDefinitionCommand alarm = fromJson(json, CreateAlarmDefinitionCommand.class);
|
||||
assertEquals(alarm, newAlarm);
|
||||
}
|
||||
}
|
||||
@@ -18,28 +18,28 @@ import static com.hpcloud.dropwizard.JsonHelpers.jsonFixture;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.domain.model.alarm.Alarm;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinition;
|
||||
import com.hpcloud.mon.domain.model.common.Link;
|
||||
|
||||
@Test
|
||||
public class AlarmTest extends AbstractModelTest {
|
||||
private final Alarm alarm;
|
||||
public class AlarmDefinitionTest extends AbstractModelTest {
|
||||
private final AlarmDefinition alarm;
|
||||
private final Map<String, String> dimensions;
|
||||
|
||||
public AlarmTest() {
|
||||
public AlarmDefinitionTest() {
|
||||
dimensions = new HashMap<String, String>();
|
||||
dimensions.put("instance_id", "666");
|
||||
dimensions.put("image_id", "345");
|
||||
alarm =
|
||||
new Alarm("123", "90% CPU", null, "LOW",
|
||||
"avg(hpcs.compute{instance_id=666, image_id=345}) >= 90", AlarmState.OK, false,
|
||||
Arrays.asList("123345345", "23423"), null, null);
|
||||
new AlarmDefinition("123", "90% CPU", null, "LOW",
|
||||
"avg(hpcs.compute{instance_id=666, image_id=345}) >= 90",
|
||||
Collections.<String>emptyList(), false, Arrays.asList("123345345", "23423"), null, null);
|
||||
alarm.setLinks(Arrays
|
||||
.asList(new Link("self", "https://region-a.geo-1.maas.hpcloudsvc.com/v1.0")));
|
||||
}
|
||||
@@ -48,10 +48,4 @@ public class AlarmTest extends AbstractModelTest {
|
||||
String json = toJson(alarm);
|
||||
assertEquals(json, jsonFixture("fixtures/alarm.json"));
|
||||
}
|
||||
|
||||
public void shouldDeserializeFromJson() throws Exception {
|
||||
String json = jsonFixture("fixtures/alarm.json");
|
||||
Alarm detail = fromJson(json, Alarm.class);
|
||||
assertEquals(alarm, detail);
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public class AlarmStateHistoryInfluxDbRepositoryImplTest {
|
||||
|
||||
public void buildQueryForFindByIdTest() throws Exception {
|
||||
|
||||
String er = "select alarm_id, old_state, new_state, reason, " +
|
||||
String er = "select alarm_id, metrics, old_state, new_state, reason, " +
|
||||
"" + "reason_data from alarm_state_history where tenant_id = 'tenant-id' and alarm_id = "
|
||||
+ "'alarm-id'";
|
||||
String r = this.alarmStateHistoryInfluxDBRepository.buildQueryForFindById("tenant-id",
|
||||
@@ -75,7 +75,7 @@ public class AlarmStateHistoryInfluxDbRepositoryImplTest {
|
||||
}
|
||||
|
||||
public void buildQueryForFindTest() throws Exception {
|
||||
String er = "select alarm_id, old_state, new_state, reason, " +
|
||||
String er = "select alarm_id, metrics, old_state, new_state, reason, " +
|
||||
"" + "reason_data from alarm_state_history where tenant_id = 'tenant-id' and time > " +
|
||||
"1388559600s and time < 1388559601s and ( alarm_id = 'id-1' or alarm_id = 'id-2' )";
|
||||
String r = this.alarmStateHistoryInfluxDBRepository.buildQueryForFind("tenant-id",
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 com.hpcloud.mon.infrastructure.persistence.mysql;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.skife.jdbi.v2.DBI;
|
||||
import org.skife.jdbi.v2.Handle;
|
||||
import org.skife.jdbi.v2.util.StringMapper;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.Resources;
|
||||
import com.hpcloud.mon.common.model.alarm.AggregateFunction;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmOperator;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmSubExpression;
|
||||
import com.hpcloud.mon.common.model.metric.MetricDefinition;
|
||||
import com.hpcloud.mon.domain.exception.EntityNotFoundException;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinition;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinitionRepository;
|
||||
|
||||
@Test(groups = "database")
|
||||
public class AlarmDefinitionMySqlRepositoryImplTest {
|
||||
private DBI db;
|
||||
private Handle handle;
|
||||
private AlarmDefinitionRepository repo;
|
||||
private List<String> alarmActions;
|
||||
|
||||
@BeforeClass
|
||||
protected void setupClass() throws Exception {
|
||||
db = new DBI("jdbc:h2:mem:test;MODE=MySQL");
|
||||
handle = db.open();
|
||||
handle
|
||||
.execute(Resources.toString(getClass().getResource("alarm.sql"), Charset.defaultCharset()));
|
||||
repo = new AlarmDefinitionMySqlRepositoryImpl(db);
|
||||
|
||||
alarmActions = new ArrayList<String>();
|
||||
alarmActions.add("29387234");
|
||||
alarmActions.add("77778687");
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
protected void afterClass() {
|
||||
handle.close();
|
||||
}
|
||||
|
||||
@BeforeMethod
|
||||
protected void beforeMethod() {
|
||||
handle.execute("SET foreign_key_checks = 0;");
|
||||
handle.execute("truncate table sub_alarm");
|
||||
handle.execute("truncate table alarm_action");
|
||||
handle.execute("truncate table sub_alarm_dimension");
|
||||
handle.execute("truncate table alarm_definition");
|
||||
|
||||
handle
|
||||
.execute("insert into alarm_definition (id, tenant_id, name, severity, expression, match_by, actions_enabled, created_at, updated_at, deleted_at) "
|
||||
+ "values ('123', 'bob', '90% CPU', 'LOW', 'avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu, device=1}) > 10', 'flavor_id,image_id', 1, NOW(), NOW(), NULL)");
|
||||
handle
|
||||
.execute("insert into sub_alarm (id, alarm_definition_id, function, metric_name, operator, threshold, period, periods, created_at, updated_at) "
|
||||
+ "values ('111', '123', 'avg', 'hpcs.compute', 'GT', 10, 60, 1, NOW(), NOW())");
|
||||
handle.execute("insert into sub_alarm_dimension values ('111', 'flavor_id', '777')");
|
||||
handle.execute("insert into sub_alarm_dimension values ('111', 'image_id', '888')");
|
||||
handle.execute("insert into sub_alarm_dimension values ('111', 'metric_name', 'cpu')");
|
||||
handle.execute("insert into sub_alarm_dimension values ('111', 'device', '1')");
|
||||
handle.execute("insert into alarm_action values ('123', 'ALARM', '29387234')");
|
||||
handle.execute("insert into alarm_action values ('123', 'ALARM', '77778687')");
|
||||
|
||||
handle
|
||||
.execute("insert into alarm_definition (id, tenant_id, name, severity, expression, match_by, actions_enabled, created_at, updated_at, deleted_at) "
|
||||
+ "values ('234', 'bob', '50% CPU', 'LOW', 'avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=mem}) > 20 and avg(hpcs.compute) < 100', 'flavor_id,image_id', 1, NOW(), NOW(), NULL)");
|
||||
handle
|
||||
.execute("insert into sub_alarm (id, alarm_definition_id, function, metric_name, operator, threshold, period, periods, created_at, updated_at) "
|
||||
+ "values ('222', '234', 'avg', 'hpcs.compute', 'GT', 20, 60, 1, NOW(), NOW())");
|
||||
handle
|
||||
.execute("insert into sub_alarm (id, alarm_definition_id, function, metric_name, operator, threshold, period, periods, created_at, updated_at) "
|
||||
+ "values ('223', '234', 'avg', 'hpcs.compute', 'LT', 100, 60, 1, NOW(), NOW())");
|
||||
handle.execute("insert into sub_alarm_dimension values ('222', 'flavor_id', '777')");
|
||||
handle.execute("insert into sub_alarm_dimension values ('222', 'image_id', '888')");
|
||||
handle.execute("insert into sub_alarm_dimension values ('222', 'metric_name', 'mem')");
|
||||
handle.execute("insert into alarm_action values ('234', 'ALARM', '29387234')");
|
||||
handle.execute("insert into alarm_action values ('234', 'ALARM', '77778687')");
|
||||
}
|
||||
|
||||
public void shouldCreate() {
|
||||
Map<String, AlarmSubExpression> subExpressions =
|
||||
ImmutableMap
|
||||
.<String, AlarmSubExpression>builder()
|
||||
.put(
|
||||
"4433",
|
||||
AlarmSubExpression
|
||||
.of("avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu}) > 10"))
|
||||
.build();
|
||||
|
||||
AlarmDefinition alarmA =
|
||||
repo.create("555", "2345", "90% CPU", null, "LOW",
|
||||
"avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu}) > 10", subExpressions,
|
||||
Arrays.asList("flavor_id", "image_id"), alarmActions, null, null);
|
||||
AlarmDefinition alarmB = repo.findById("555", alarmA.getId());
|
||||
|
||||
assertEquals(alarmA, alarmB);
|
||||
|
||||
// Assert that sub-alarm and sub-alarm-dimensions made it to the db
|
||||
assertEquals(
|
||||
handle.createQuery("select count(*) from sub_alarm where id = 4433")
|
||||
.map(StringMapper.FIRST).first(), "1");
|
||||
assertEquals(
|
||||
handle.createQuery("select count(*) from sub_alarm_dimension where sub_alarm_id = 4433")
|
||||
.map(StringMapper.FIRST).first(), "3");
|
||||
}
|
||||
|
||||
@Test(groups = "database")
|
||||
public void shouldUpdate() {
|
||||
db = new DBI("jdbc:mysql://192.168.10.4/mon", "monapi", "password");
|
||||
handle = db.open();
|
||||
repo = new AlarmDefinitionMySqlRepositoryImpl(db);
|
||||
beforeMethod();
|
||||
|
||||
List<String> oldSubAlarmIds = Arrays.asList("222");
|
||||
AlarmSubExpression changedSubExpression = AlarmSubExpression.of("avg(hpcs.compute) <= 200");
|
||||
Map<String, AlarmSubExpression> changedSubExpressions =
|
||||
ImmutableMap.<String, AlarmSubExpression>builder().put("223", changedSubExpression).build();
|
||||
AlarmSubExpression newSubExpression = AlarmSubExpression.of("avg(foo{flavor_id=777}) > 333");
|
||||
Map<String, AlarmSubExpression> newSubExpressions =
|
||||
ImmutableMap.<String, AlarmSubExpression>builder().put("555", newSubExpression).build();
|
||||
|
||||
repo.update("bob", "234", false, "90% CPU", null,
|
||||
"avg(foo{flavor_id=777}) > 333 and avg(hpcs.compute) <= 200",
|
||||
Arrays.asList("flavor_id", "image_id"), "LOW", false, oldSubAlarmIds,
|
||||
changedSubExpressions, newSubExpressions, alarmActions, null, null);
|
||||
|
||||
AlarmDefinition alarm = repo.findById("bob", "234");
|
||||
AlarmDefinition expected =
|
||||
new AlarmDefinition("234", "90% CPU", null, "LOW",
|
||||
"avg(foo{flavor_id=777}) > 333 and avg(hpcs.compute) <= 200", Arrays.asList(
|
||||
"flavor_id", "image_id"), false, alarmActions, Collections.<String>emptyList(),
|
||||
Collections.<String>emptyList());
|
||||
assertEquals(expected, alarm);
|
||||
|
||||
Map<String, AlarmSubExpression> subExpressions = repo.findSubExpressions("234");
|
||||
assertEquals(subExpressions.get("223"), changedSubExpression);
|
||||
assertEquals(subExpressions.get("555"), newSubExpression);
|
||||
}
|
||||
|
||||
public void shouldFindById() {
|
||||
AlarmDefinition alarm = repo.findById("bob", "123");
|
||||
|
||||
assertEquals(alarm.getId(), "123");
|
||||
assertEquals(alarm.getName(), "90% CPU");
|
||||
assertEquals(alarm.getSeverity(), "LOW");
|
||||
assertEquals(alarm.getExpression(),
|
||||
"avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu, device=1}) > 10");
|
||||
assertEquals(alarm.getMatchBy(), Arrays.asList("flavor_id", "image_id"));
|
||||
assertEquals(alarm.isActionsEnabled(), true);
|
||||
assertEquals(alarm.getAlarmActions(), alarmActions);
|
||||
}
|
||||
|
||||
@Test(groups = "database")
|
||||
public void shouldFindSubAlarmMetricDefinitions() {
|
||||
db = new DBI("jdbc:mysql://192.168.10.4/mon", "monapi", "password");
|
||||
handle = db.open();
|
||||
repo = new AlarmDefinitionMySqlRepositoryImpl(db);
|
||||
beforeMethod();
|
||||
|
||||
assertEquals(
|
||||
repo.findSubAlarmMetricDefinitions("123").get("111"),
|
||||
new MetricDefinition("hpcs.compute", ImmutableMap.<String, String>builder()
|
||||
.put("flavor_id", "777").put("image_id", "888").put("metric_name", "cpu")
|
||||
.put("device", "1").build()));
|
||||
|
||||
assertEquals(
|
||||
repo.findSubAlarmMetricDefinitions("234").get("222"),
|
||||
new MetricDefinition("hpcs.compute", ImmutableMap.<String, String>builder()
|
||||
.put("flavor_id", "777").put("image_id", "888").put("metric_name", "mem").build()));
|
||||
|
||||
assertTrue(repo.findSubAlarmMetricDefinitions("asdfasdf").isEmpty());
|
||||
}
|
||||
|
||||
@Test(groups = "database")
|
||||
public void shouldFindSubExpressions() {
|
||||
db = new DBI("jdbc:mysql://192.168.10.4/mon", "monapi", "password");
|
||||
handle = db.open();
|
||||
repo = new AlarmDefinitionMySqlRepositoryImpl(db);
|
||||
beforeMethod();
|
||||
|
||||
assertEquals(
|
||||
repo.findSubExpressions("123").get("111"),
|
||||
new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute",
|
||||
ImmutableMap.<String, String>builder().put("flavor_id", "777").put("image_id", "888")
|
||||
.put("metric_name", "cpu").put("device", "1").build()), AlarmOperator.GT, 10, 60, 1));
|
||||
|
||||
assertEquals(repo.findSubExpressions("234").get("223"), new AlarmSubExpression(
|
||||
AggregateFunction.AVG, new MetricDefinition("hpcs.compute", null), AlarmOperator.LT, 100,
|
||||
60, 1));
|
||||
|
||||
assertTrue(repo.findSubAlarmMetricDefinitions("asdfasdf").isEmpty());
|
||||
}
|
||||
|
||||
public void testExists() {
|
||||
assertTrue(repo.exists("bob", "90% CPU"));
|
||||
|
||||
// Negative
|
||||
assertFalse(repo.exists("bob", "999% CPU"));
|
||||
}
|
||||
|
||||
public void shouldFind() {
|
||||
List<AlarmDefinition> alarms = repo.find("bob", null, null);
|
||||
|
||||
assertEquals(
|
||||
alarms,
|
||||
Arrays.asList(
|
||||
new AlarmDefinition("123", "90% CPU", null, "LOW",
|
||||
"avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu, device=1}) > 10",
|
||||
Arrays.asList("flavor_id", "image_id"), true,
|
||||
Arrays.asList("29387234", "77778687"), Collections.<String>emptyList(), Collections
|
||||
.<String>emptyList()),
|
||||
new AlarmDefinition(
|
||||
"234",
|
||||
"50% CPU",
|
||||
null,
|
||||
"LOW",
|
||||
"avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=mem}) > 20 and avg(hpcs.compute) < 100",
|
||||
Arrays.asList("flavor_id", "image_id"), true,
|
||||
Arrays.asList("29387234", "77778687"), Collections.<String>emptyList(), Collections
|
||||
.<String>emptyList())));
|
||||
}
|
||||
|
||||
public void shouldFindByName() {
|
||||
List<AlarmDefinition> alarms = repo.find("bob", "90% CPU", null);
|
||||
|
||||
assertEquals(alarms, Arrays.asList(new AlarmDefinition("123", "90% CPU", null, "LOW",
|
||||
"avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu, device=1}) > 10", Arrays
|
||||
.asList("flavor_id", "image_id"), true, Arrays.asList("29387234", "77778687"),
|
||||
Collections.<String>emptyList(), Collections.<String>emptyList())));
|
||||
}
|
||||
|
||||
public void shouldDeleteById() {
|
||||
repo.deleteById("bob", "123");
|
||||
|
||||
try {
|
||||
assertNull(repo.findById("bob", "123"));
|
||||
fail();
|
||||
} catch (EntityNotFoundException expected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,21 +15,16 @@
|
||||
package com.hpcloud.mon.infrastructure.persistence.mysql;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.skife.jdbi.v2.DBI;
|
||||
import org.skife.jdbi.v2.Handle;
|
||||
import org.skife.jdbi.v2.util.StringMapper;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
@@ -37,10 +32,7 @@ import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.Resources;
|
||||
import com.hpcloud.mon.common.model.alarm.AggregateFunction;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmOperator;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmSubExpression;
|
||||
import com.hpcloud.mon.common.model.metric.MetricDefinition;
|
||||
import com.hpcloud.mon.domain.exception.EntityNotFoundException;
|
||||
import com.hpcloud.mon.domain.model.alarm.Alarm;
|
||||
@@ -74,195 +66,85 @@ public class AlarmMySqlRepositoryImplTest {
|
||||
@BeforeMethod
|
||||
protected void beforeMethod() {
|
||||
handle.execute("SET foreign_key_checks = 0;");
|
||||
handle.execute("truncate table sub_alarm");
|
||||
handle.execute("truncate table alarm_action");
|
||||
handle.execute("truncate table sub_alarm_dimension");
|
||||
handle.execute("truncate table alarm");
|
||||
handle.execute("truncate table alarm_action");
|
||||
handle.execute("truncate table alarm_definition");
|
||||
handle.execute("truncate table alarm_metric");
|
||||
handle.execute("truncate table metric_definition");
|
||||
handle.execute("truncate table metric_definition_dimensions");
|
||||
handle.execute("truncate table metric_dimension");
|
||||
|
||||
handle
|
||||
.execute("insert into alarm (id, tenant_id, name, severity, expression, state, actions_enabled, created_at, updated_at, deleted_at) "
|
||||
+ "values ('123', 'bob', '90% CPU', 'LOW', 'avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu, device=1}) > 10', 'UNDETERMINED', 1, NOW(), NOW(), NULL)");
|
||||
.execute("insert into alarm_definition (id, tenant_id, name, severity, expression, match_by, actions_enabled, created_at, updated_at, deleted_at) "
|
||||
+ "values ('1', 'bob', '90% CPU', 'LOW', 'avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu, device=1}) > 10', 'flavor_id,image_id', 1, NOW(), NOW(), NULL)");
|
||||
handle
|
||||
.execute("insert into sub_alarm (id, alarm_id, function, metric_name, operator, threshold, period, periods, state, created_at, updated_at) "
|
||||
+ "values ('111', '123', 'avg', 'hpcs.compute', 'GT', 10, 60, 1, 'UNDETERMINED', NOW(), NOW())");
|
||||
handle.execute("insert into sub_alarm_dimension values ('111', 'flavor_id', '777')");
|
||||
handle.execute("insert into sub_alarm_dimension values ('111', 'image_id', '888')");
|
||||
handle.execute("insert into sub_alarm_dimension values ('111', 'metric_name', 'cpu')");
|
||||
handle.execute("insert into sub_alarm_dimension values ('111', 'device', '1')");
|
||||
handle.execute("insert into alarm_action values ('123', 'ALARM', '29387234')");
|
||||
handle.execute("insert into alarm_action values ('123', 'ALARM', '77778687')");
|
||||
.execute("insert into alarm (id, alarm_definition_id, state, created_at, updated_at) values ('1', '1', 'OK', NOW(), NOW())");
|
||||
handle
|
||||
.execute("insert into alarm (id, alarm_definition_id, state, created_at, updated_at) values ('2', '1', 'UNDETERMINED', NOW(), NOW())");
|
||||
handle
|
||||
.execute("insert into alarm (id, alarm_definition_id, state, created_at, updated_at) values ('3', '1', 'ALARM', NOW(), NOW())");
|
||||
handle
|
||||
.execute("insert into alarm_metric (alarm_id, metric_definition_dimensions_id) values ('1', 11)");
|
||||
handle
|
||||
.execute("insert into alarm_metric (alarm_id, metric_definition_dimensions_id) values ('1', 22)");
|
||||
handle
|
||||
.execute("insert into alarm_metric (alarm_id, metric_definition_dimensions_id) values ('2', 11)");
|
||||
handle
|
||||
.execute("insert into alarm_metric (alarm_id, metric_definition_dimensions_id) values ('3', 22)");
|
||||
handle
|
||||
.execute("insert into metric_definition (id, name, tenant_id, region) values (1, 'cpu', 'bob', 'west')");
|
||||
handle
|
||||
.execute("insert into metric_definition (id, name, tenant_id, region) values (2, 'mem', 'bob', 'west')");
|
||||
handle
|
||||
.execute("insert into metric_definition_dimensions (id, metric_definition_id, metric_dimension_set_id) values (11, 1, 1)");
|
||||
handle
|
||||
.execute("insert into metric_definition_dimensions (id, metric_definition_id, metric_dimension_set_id) values (22, 2, 1)");
|
||||
handle
|
||||
.execute("insert into metric_dimension (dimension_set_id, name, value) values (1, 'instance_id', '123')");
|
||||
handle
|
||||
.execute("insert into metric_dimension (dimension_set_id, name, value) values (1, 'flavor_id', '222')");
|
||||
|
||||
handle
|
||||
.execute("insert into alarm (id, tenant_id, name, severity, expression, state, actions_enabled, created_at, updated_at, deleted_at) "
|
||||
+ "values ('234', 'bob', '50% CPU', 'LOW', 'avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=mem}) > 20 and avg(hpcs.compute) < 100', 'UNDETERMINED', 1, NOW(), NOW(), NULL)");
|
||||
.execute("insert into alarm_definition (id, tenant_id, name, severity, expression, match_by, actions_enabled, created_at, updated_at, deleted_at) "
|
||||
+ "values ('234', 'bob', '50% CPU', 'LOW', 'avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=mem}) > 20 and avg(hpcs.compute) < 100', 'flavor_id,image_id', 1, NOW(), NOW(), NULL)");
|
||||
handle
|
||||
.execute("insert into sub_alarm (id, alarm_id, function, metric_name, operator, threshold, period, periods, state, created_at, updated_at) "
|
||||
+ "values ('222', '234', 'avg', 'hpcs.compute', 'GT', 20, 60, 1, 'UNDETERMINED', NOW(), NOW())");
|
||||
.execute("insert into alarm (id, alarm_definition_id, state, created_at, updated_at) values ('234111', '234', 'UNDETERMINED', NOW(), NOW())");
|
||||
handle
|
||||
.execute("insert into sub_alarm (id, alarm_id, function, metric_name, operator, threshold, period, periods, state, created_at, updated_at) "
|
||||
+ "values ('223', '234', 'avg', 'hpcs.compute', 'LT', 100, 60, 1, 'UNDETERMINED', NOW(), NOW())");
|
||||
handle.execute("insert into sub_alarm_dimension values ('222', 'flavor_id', '777')");
|
||||
handle.execute("insert into sub_alarm_dimension values ('222', 'image_id', '888')");
|
||||
handle.execute("insert into sub_alarm_dimension values ('222', 'metric_name', 'mem')");
|
||||
handle.execute("insert into alarm_action values ('234', 'ALARM', '29387234')");
|
||||
handle.execute("insert into alarm_action values ('234', 'ALARM', '77778687')");
|
||||
}
|
||||
|
||||
public void shouldCreate() {
|
||||
Map<String, AlarmSubExpression> subExpressions =
|
||||
ImmutableMap
|
||||
.<String, AlarmSubExpression>builder()
|
||||
.put(
|
||||
"4433",
|
||||
AlarmSubExpression
|
||||
.of("avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu}) > 10"))
|
||||
.build();
|
||||
|
||||
Alarm alarmA =
|
||||
repo.create("555", "2345", "90% CPU", null, "LOW",
|
||||
"avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu}) > 10", subExpressions,
|
||||
alarmActions, null, null);
|
||||
Alarm alarmB = repo.findById("555", alarmA.getId());
|
||||
|
||||
assertEquals(alarmA, alarmB);
|
||||
|
||||
// Assert that sub-alarm and sub-alarm-dimensions made it to the db
|
||||
assertEquals(
|
||||
handle.createQuery("select count(*) from sub_alarm where id = 4433")
|
||||
.map(StringMapper.FIRST).first(), "1");
|
||||
assertEquals(
|
||||
handle.createQuery("select count(*) from sub_alarm_dimension where sub_alarm_id = 4433")
|
||||
.map(StringMapper.FIRST).first(), "3");
|
||||
.execute("insert into alarm (id, alarm_definition_id, state, created_at, updated_at) values ('234222', '234', 'ALARM', NOW(), NOW())");
|
||||
}
|
||||
|
||||
@Test(groups = "database")
|
||||
public void shouldUpdate() {
|
||||
db = new DBI("jdbc:mysql://192.168.10.4/mon", "monapi", "password");
|
||||
handle = db.open();
|
||||
repo = new AlarmMySqlRepositoryImpl(db);
|
||||
beforeMethod();
|
||||
|
||||
List<String> oldSubAlarmIds = Arrays.asList("222");
|
||||
AlarmSubExpression changedSubExpression = AlarmSubExpression.of("avg(hpcs.compute) <= 200");
|
||||
Map<String, AlarmSubExpression> changedSubExpressions =
|
||||
ImmutableMap.<String, AlarmSubExpression>builder().put("223", changedSubExpression).build();
|
||||
AlarmSubExpression newSubExpression = AlarmSubExpression.of("avg(foo{flavor_id=777}) > 333");
|
||||
Map<String, AlarmSubExpression> newSubExpressions =
|
||||
ImmutableMap.<String, AlarmSubExpression>builder().put("555", newSubExpression).build();
|
||||
|
||||
repo.update("bob", "234", false, "90% CPU", null,
|
||||
"avg(foo{flavor_id=777}) > 333 and avg(hpcs.compute) <= 200", "LOW", AlarmState.ALARM,
|
||||
false, oldSubAlarmIds, changedSubExpressions, newSubExpressions, alarmActions, null, null);
|
||||
|
||||
Alarm alarm = repo.findById("bob", "234");
|
||||
Alarm expected =
|
||||
new Alarm("234", "90% CPU", null, "LOW",
|
||||
"avg(foo{flavor_id=777}) > 333 and avg(hpcs.compute) <= 200", AlarmState.ALARM, false,
|
||||
alarmActions, Collections.<String>emptyList(), Collections.<String>emptyList());
|
||||
assertEquals(expected, alarm);
|
||||
|
||||
Map<String, AlarmSubExpression> subExpressions = repo.findSubExpressions("234");
|
||||
assertEquals(subExpressions.get("223"), changedSubExpression);
|
||||
assertEquals(subExpressions.get("555"), newSubExpression);
|
||||
}
|
||||
|
||||
public void shouldFindById() {
|
||||
Alarm alarm = repo.findById("bob", "123");
|
||||
|
||||
assertEquals(alarm.getId(), "123");
|
||||
assertEquals(alarm.getName(), "90% CPU");
|
||||
assertEquals(alarm.getSeverity(), "LOW");
|
||||
assertEquals(alarm.getExpression(),
|
||||
"avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu, device=1}) > 10");
|
||||
assertEquals(alarm.getState(), AlarmState.UNDETERMINED);
|
||||
assertEquals(alarm.isActionsEnabled(), true);
|
||||
assertEquals(alarm.getAlarmActions(), alarmActions);
|
||||
}
|
||||
|
||||
@Test(groups = "database")
|
||||
public void shouldFindSubAlarmMetricDefinitions() {
|
||||
db = new DBI("jdbc:mysql://192.168.10.4/mon", "monapi", "password");
|
||||
handle = db.open();
|
||||
repo = new AlarmMySqlRepositoryImpl(db);
|
||||
beforeMethod();
|
||||
|
||||
assertEquals(
|
||||
repo.findSubAlarmMetricDefinitions("123").get("111"),
|
||||
new MetricDefinition("hpcs.compute", ImmutableMap.<String, String>builder()
|
||||
.put("flavor_id", "777").put("image_id", "888").put("metric_name", "cpu")
|
||||
.put("device", "1").build()));
|
||||
|
||||
assertEquals(
|
||||
repo.findSubAlarmMetricDefinitions("234").get("222"),
|
||||
new MetricDefinition("hpcs.compute", ImmutableMap.<String, String>builder()
|
||||
.put("flavor_id", "777").put("image_id", "888").put("metric_name", "mem").build()));
|
||||
|
||||
assertTrue(repo.findSubAlarmMetricDefinitions("asdfasdf").isEmpty());
|
||||
}
|
||||
|
||||
@Test(groups = "database")
|
||||
public void shouldFindSubExpressions() {
|
||||
db = new DBI("jdbc:mysql://192.168.10.4/mon", "monapi", "password");
|
||||
handle = db.open();
|
||||
repo = new AlarmMySqlRepositoryImpl(db);
|
||||
beforeMethod();
|
||||
|
||||
assertEquals(
|
||||
repo.findSubExpressions("123").get("111"),
|
||||
new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute",
|
||||
ImmutableMap.<String, String>builder().put("flavor_id", "777").put("image_id", "888")
|
||||
.put("metric_name", "cpu").put("device", "1").build()), AlarmOperator.GT, 10, 60, 1));
|
||||
|
||||
assertEquals(repo.findSubExpressions("234").get("223"), new AlarmSubExpression(
|
||||
AggregateFunction.AVG, new MetricDefinition("hpcs.compute", null), AlarmOperator.LT, 100,
|
||||
60, 1));
|
||||
|
||||
assertTrue(repo.findSubAlarmMetricDefinitions("asdfasdf").isEmpty());
|
||||
}
|
||||
|
||||
public void testExists() {
|
||||
assertTrue(repo.exists("bob", "90% CPU"));
|
||||
|
||||
// Negative
|
||||
assertFalse(repo.exists("bob", "999% CPU"));
|
||||
}
|
||||
|
||||
public void shouldFind() {
|
||||
List<Alarm> alarms = repo.find("bob", null, null, null);
|
||||
|
||||
assertEquals(
|
||||
alarms,
|
||||
Arrays.asList(
|
||||
new Alarm("123", "90% CPU", null, "LOW",
|
||||
"avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu, device=1}) > 10",
|
||||
AlarmState.UNDETERMINED, true, Arrays.asList("29387234", "77778687"), Collections
|
||||
.<String>emptyList(), Collections.<String>emptyList()),
|
||||
new Alarm(
|
||||
"234",
|
||||
"50% CPU",
|
||||
null,
|
||||
"LOW",
|
||||
"avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=mem}) > 20 and avg(hpcs.compute) < 100",
|
||||
AlarmState.UNDETERMINED, true, Arrays.asList("29387234", "77778687"), Collections
|
||||
.<String>emptyList(), Collections.<String>emptyList())));
|
||||
}
|
||||
|
||||
public void shouldFindByName() {
|
||||
List<Alarm> alarms = repo.find("bob", "90% CPU", null, null);
|
||||
|
||||
assertEquals(alarms, Arrays.asList(new Alarm("123", "90% CPU", null, "LOW",
|
||||
"avg(hpcs.compute{flavor_id=777, image_id=888, metric_name=cpu, device=1}) > 10",
|
||||
AlarmState.UNDETERMINED, true, Arrays.asList("29387234", "77778687"), Collections
|
||||
.<String>emptyList(), Collections.<String>emptyList())));
|
||||
}
|
||||
|
||||
public void shouldDeleteById() {
|
||||
repo.deleteById("bob", "123");
|
||||
public void shouldDelete() {
|
||||
repo.deleteById("123111");
|
||||
|
||||
try {
|
||||
assertNull(repo.findById("bob", "123"));
|
||||
assertNull(repo.findById("123111"));
|
||||
fail();
|
||||
} catch (EntityNotFoundException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = "database")
|
||||
public void shouldFind() {
|
||||
List<Alarm> alarms =
|
||||
repo.find("bob", "1", "cpu",
|
||||
ImmutableMap.<String, String>builder().put("instance_id", "123").build(), null);
|
||||
assertEquals(alarms, Arrays.asList(new Alarm("123111", "123", "90% CPU", Arrays
|
||||
.asList(new MetricDefinition("hpcs.compute", ImmutableMap.<String, String>builder()
|
||||
.put("flavor_id", "777").put("image_id", "888").put("metric_name", "cpu").build())),
|
||||
AlarmState.ALARM)));
|
||||
}
|
||||
|
||||
@Test(groups = "database")
|
||||
public void shouldFindById() {
|
||||
Alarm alarm = repo.findById("123111");
|
||||
|
||||
assertEquals(alarm.getId(), "123111");
|
||||
assertEquals(alarm.getAlarmDefinitionId(), "123");
|
||||
assertEquals(alarm.getState(), "90% CPU");
|
||||
assertEquals(
|
||||
alarm.getMetrics(),
|
||||
Arrays.asList(new MetricDefinition("hpcs.compute", ImmutableMap.<String, String>builder()
|
||||
.put("flavor_id", "777").put("image_id", "888").put("metric_name", "cpu").build())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.testng.Assert.fail;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -43,28 +44,27 @@ import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Names;
|
||||
import com.hpcloud.mon.MonApiConfiguration;
|
||||
import com.hpcloud.mon.MonApiModule;
|
||||
import com.hpcloud.mon.app.AlarmService;
|
||||
import com.hpcloud.mon.app.command.CreateAlarmCommand;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.app.AlarmDefinitionService;
|
||||
import com.hpcloud.mon.app.command.CreateAlarmDefinitionCommand;
|
||||
import com.hpcloud.mon.domain.exception.EntityNotFoundException;
|
||||
import com.hpcloud.mon.domain.model.alarm.Alarm;
|
||||
import com.hpcloud.mon.domain.model.alarm.AlarmRepository;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinition;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinitionRepository;
|
||||
import com.hpcloud.mon.domain.model.alarmstatehistory.AlarmStateHistoryRepository;
|
||||
import com.hpcloud.mon.infrastructure.persistence.mysql.AlarmMySqlRepositoryImpl;
|
||||
import com.hpcloud.mon.infrastructure.persistence.mysql.AlarmDefinitionMySqlRepositoryImpl;
|
||||
import com.hpcloud.mon.infrastructure.persistence.mysql.NotificationMethodMySqlRepositoryImpl;
|
||||
import com.hpcloud.mon.resource.AbstractMonApiResourceTest;
|
||||
import com.hpcloud.mon.resource.AlarmResource;
|
||||
import com.hpcloud.mon.resource.AlarmDefinitionResource;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
|
||||
@Test(groups = "integration", enabled = false)
|
||||
public class AlarmIntegrationTest extends AbstractMonApiResourceTest {
|
||||
private static final String TENANT_ID = "alarm-test";
|
||||
private DBI mysqlDb;
|
||||
private Alarm alarm;
|
||||
private AlarmService service;
|
||||
private AlarmDefinition alarm;
|
||||
private AlarmDefinitionService service;
|
||||
private MonApiConfiguration config;
|
||||
private Producer<String, String> producer;
|
||||
private AlarmRepository repo;
|
||||
private AlarmDefinitionRepository repo;
|
||||
AlarmStateHistoryRepository stateHistoryRepo;
|
||||
private Map<String, String> dimensions;
|
||||
private List<String> alarmActions;
|
||||
@@ -82,10 +82,11 @@ public class AlarmIntegrationTest extends AbstractMonApiResourceTest {
|
||||
.execute("insert into notification_method (id, tenant_id, name, type, address, created_at, updated_at) values ('77778687', 'alarm-test', 'MySMS', 'SMS', '8675309', NOW(), NOW())");
|
||||
mysqlDb.close(handle);
|
||||
|
||||
repo = new AlarmMySqlRepositoryImpl(mysqlDb);
|
||||
repo = new AlarmDefinitionMySqlRepositoryImpl(mysqlDb);
|
||||
service =
|
||||
new AlarmService(config, producer, repo, new NotificationMethodMySqlRepositoryImpl(mysqlDb));
|
||||
addResources(new AlarmResource(service, repo, null));
|
||||
new AlarmDefinitionService(config, producer, repo,
|
||||
new NotificationMethodMySqlRepositoryImpl(mysqlDb));
|
||||
addResources(new AlarmDefinitionResource(service, repo));
|
||||
}
|
||||
|
||||
@BeforeTest
|
||||
@@ -110,8 +111,9 @@ public class AlarmIntegrationTest extends AbstractMonApiResourceTest {
|
||||
alarmActions.add("29387234");
|
||||
alarmActions.add("77778687");
|
||||
alarm =
|
||||
new Alarm("123", "90% CPU", null, null, "avg(hpcs.compute:cpu:{instance_id=123} > 10",
|
||||
AlarmState.OK, true, alarmActions, null, null);
|
||||
new AlarmDefinition("123", "90% CPU", null, null,
|
||||
"avg(hpcs.compute:cpu:{instance_id=123} > 10", Arrays.asList("instance_id"), true,
|
||||
alarmActions, null, null);
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
@@ -127,10 +129,11 @@ public class AlarmIntegrationTest extends AbstractMonApiResourceTest {
|
||||
.header("Content-Type", MediaType.APPLICATION_JSON)
|
||||
.post(
|
||||
ClientResponse.class,
|
||||
new CreateAlarmCommand("90% CPU", null, null,
|
||||
"avg(hpcs.compute:cpu:{instance_id=123} > 10", alarmActions, null, null));
|
||||
new CreateAlarmDefinitionCommand("90% CPU", null,
|
||||
"avg(hpcs.compute:cpu:{instance_id=123} > 10", Arrays.asList("instance_id"),
|
||||
null, alarmActions, null, null));
|
||||
|
||||
Alarm newAlarm = response.getEntity(Alarm.class);
|
||||
AlarmDefinition newAlarm = response.getEntity(AlarmDefinition.class);
|
||||
String location = response.getHeaders().get("Location").get(0);
|
||||
assertEquals(response.getStatus(), 201);
|
||||
assertEquals(location, "/v2.0/alarms/" + newAlarm.getId());
|
||||
@@ -139,10 +142,10 @@ public class AlarmIntegrationTest extends AbstractMonApiResourceTest {
|
||||
}
|
||||
|
||||
public void shouldCreateCaseInsensitiveAndKeywords() throws Exception {
|
||||
Alarm alarm_local;
|
||||
AlarmDefinition alarm_local;
|
||||
alarm_local =
|
||||
new Alarm("123", "90% CPU", null, null, "AvG(avg:cpu:{instance_id=123} gT 10",
|
||||
AlarmState.OK, true, alarmActions, null, null);
|
||||
new AlarmDefinition("123", "90% CPU", null, null, "AvG(avg:cpu:{instance_id=123} gT 10",
|
||||
Arrays.asList("instance_id"), true, alarmActions, null, null);
|
||||
ClientResponse response =
|
||||
client()
|
||||
.resource("/v2.0/alarms")
|
||||
@@ -150,10 +153,11 @@ public class AlarmIntegrationTest extends AbstractMonApiResourceTest {
|
||||
.header("Content-Type", MediaType.APPLICATION_JSON)
|
||||
.post(
|
||||
ClientResponse.class,
|
||||
new CreateAlarmCommand("90% CPU", null, null,
|
||||
"AvG(avg:cpu:{instance_id=123} gT 10", alarmActions, null, null));
|
||||
new CreateAlarmDefinitionCommand("90% CPU", null,
|
||||
"AvG(avg:cpu:{instance_id=123} gT 10", Arrays.asList("instance_id"), null,
|
||||
alarmActions, null, null));
|
||||
|
||||
Alarm newAlarm = response.getEntity(Alarm.class);
|
||||
AlarmDefinition newAlarm = response.getEntity(AlarmDefinition.class);
|
||||
String location = response.getHeaders().get("Location").get(0);
|
||||
assertEquals(response.getStatus(), 201);
|
||||
assertEquals(location, "/v2.0/alarms/" + newAlarm.getId());
|
||||
@@ -162,10 +166,10 @@ public class AlarmIntegrationTest extends AbstractMonApiResourceTest {
|
||||
}
|
||||
|
||||
public void shouldDelete() {
|
||||
Alarm newAlarm =
|
||||
repo.create(TENANT_ID, "123", alarm.getName(), null, alarm.getName(),
|
||||
alarm.getExpression(), null, alarm.getAlarmActions(), alarm.getOkActions(),
|
||||
alarm.getUndeterminedActions());
|
||||
AlarmDefinition newAlarm =
|
||||
repo.create(TENANT_ID, "123", alarm.getName(), alarm.getName(), alarm.getSeverity(),
|
||||
alarm.getExpression(), null, alarm.getMatchBy(), alarm.getAlarmActions(),
|
||||
alarm.getOkActions(), alarm.getUndeterminedActions());
|
||||
assertNotNull(repo.findById(TENANT_ID, newAlarm.getId()));
|
||||
|
||||
ClientResponse response =
|
||||
|
||||
@@ -35,33 +35,27 @@ import java.util.Map;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.hpcloud.mon.app.AlarmService;
|
||||
import com.hpcloud.mon.app.command.CreateAlarmCommand;
|
||||
import com.hpcloud.mon.app.command.UpdateAlarmCommand;
|
||||
import com.hpcloud.mon.app.AlarmDefinitionService;
|
||||
import com.hpcloud.mon.app.command.CreateAlarmDefinitionCommand;
|
||||
import com.hpcloud.mon.app.command.UpdateAlarmDefinitionCommand;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmExpression;
|
||||
import com.hpcloud.mon.common.model.alarm.AlarmState;
|
||||
import com.hpcloud.mon.domain.exception.EntityNotFoundException;
|
||||
import com.hpcloud.mon.domain.model.alarm.Alarm;
|
||||
import com.hpcloud.mon.domain.model.alarm.AlarmRepository;
|
||||
import com.hpcloud.mon.domain.model.alarmstatehistory.AlarmStateHistory;
|
||||
import com.hpcloud.mon.domain.model.alarmstatehistory.AlarmStateHistoryRepository;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinition;
|
||||
import com.hpcloud.mon.domain.model.alarmdefinition.AlarmDefinitionRepository;
|
||||
import com.hpcloud.mon.domain.model.common.Link;
|
||||
import com.hpcloud.mon.resource.exception.ErrorMessages;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.GenericType;
|
||||
|
||||
@Test
|
||||
public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
public class AlarmDefinitionResourceTest extends AbstractMonApiResourceTest {
|
||||
private String expression;
|
||||
private Alarm alarm;
|
||||
private Alarm alarmItem;
|
||||
private AlarmService service;
|
||||
private AlarmRepository repo;
|
||||
private AlarmStateHistoryRepository stateHistoryRepo;
|
||||
private AlarmDefinition alarm;
|
||||
private AlarmDefinition alarmItem;
|
||||
private AlarmDefinitionService service;
|
||||
private AlarmDefinitionRepository repo;
|
||||
private List<String> alarmActions;
|
||||
|
||||
@Override
|
||||
@@ -70,72 +64,72 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
super.setupResources();
|
||||
|
||||
expression = "avg(disk_read_ops{service=hpcs.compute, instance_id=937}) >= 90";
|
||||
List<String> matchBy = Arrays.asList("service", "instance_id");
|
||||
alarmItem =
|
||||
new Alarm("123", "Disk Exceeds 1k Operations", null, "LOW", expression, AlarmState.OK,
|
||||
true, null, null, null);
|
||||
new AlarmDefinition("123", "Disk Exceeds 1k Operations", null, "LOW", expression,
|
||||
Arrays.asList("service", "instance_id"), true, null, null, null);
|
||||
alarmActions = new ArrayList<String>();
|
||||
alarmActions.add("29387234");
|
||||
alarmActions.add("77778687");
|
||||
alarm =
|
||||
new Alarm("123", "Disk Exceeds 1k Operations", null, "LOW", expression, AlarmState.OK,
|
||||
new AlarmDefinition("123", "Disk Exceeds 1k Operations", null, "LOW", expression, matchBy,
|
||||
true, alarmActions, null, null);
|
||||
|
||||
service = mock(AlarmService.class);
|
||||
service = mock(AlarmDefinitionService.class);
|
||||
when(
|
||||
service.create(eq("abc"), eq("Disk Exceeds 1k Operations"), any(String.class), eq("LOW"),
|
||||
eq(expression), eq(AlarmExpression.of(expression)), any(List.class), any(List.class),
|
||||
any(List.class))).thenReturn(alarm);
|
||||
eq(expression), eq(AlarmExpression.of(expression)), eq(matchBy), any(List.class),
|
||||
any(List.class), any(List.class))).thenReturn(alarm);
|
||||
|
||||
repo = mock(AlarmRepository.class);
|
||||
repo = mock(AlarmDefinitionRepository.class);
|
||||
when(repo.findById(eq("abc"), eq("123"))).thenReturn(alarm);
|
||||
when(repo.find(anyString(), anyString(), (Map<String, String>) anyMap(), any(String.class)))
|
||||
.thenReturn(Arrays.asList(alarmItem));
|
||||
when(repo.find(anyString(), anyString(), (Map<String, String>) anyMap())).thenReturn(
|
||||
Arrays.asList(alarmItem));
|
||||
|
||||
stateHistoryRepo = mock(AlarmStateHistoryRepository.class);
|
||||
|
||||
addResources(new AlarmResource(service, repo, stateHistoryRepo));
|
||||
addResources(new AlarmDefinitionResource(service, repo));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreate() {
|
||||
ClientResponse response =
|
||||
createResponseFor(new CreateAlarmCommand("Disk Exceeds 1k Operations", null, expression,
|
||||
"LOW", alarmActions, null, null));
|
||||
createResponseFor(new CreateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null,
|
||||
expression, Arrays.asList("service", "instance_id"), "LOW", alarmActions, null, null));
|
||||
|
||||
assertEquals(response.getStatus(), 201);
|
||||
Alarm newAlarm = response.getEntity(Alarm.class);
|
||||
AlarmDefinition newAlarm = response.getEntity(AlarmDefinition.class);
|
||||
String location = response.getHeaders().get("Location").get(0);
|
||||
assertEquals(location, "/v2.0/alarms/" + newAlarm.getId());
|
||||
assertEquals(location, "/v2.0/alarm-definitions/" + newAlarm.getId());
|
||||
assertEquals(newAlarm, alarm);
|
||||
verify(service).create(eq("abc"), eq("Disk Exceeds 1k Operations"), any(String.class),
|
||||
eq("LOW"), eq(expression), eq(AlarmExpression.of(expression)), any(List.class),
|
||||
any(List.class), any(List.class));
|
||||
eq("LOW"), eq(expression), eq(AlarmExpression.of(expression)),
|
||||
eq(Arrays.asList("service", "instance_id")), any(List.class), any(List.class),
|
||||
any(List.class));
|
||||
}
|
||||
|
||||
public void shouldUpdate() {
|
||||
when(
|
||||
service.update(eq("abc"), eq("123"), any(AlarmExpression.class),
|
||||
any(UpdateAlarmCommand.class))).thenReturn(alarm);
|
||||
any(UpdateAlarmDefinitionCommand.class))).thenReturn(alarm);
|
||||
ClientResponse response =
|
||||
client()
|
||||
.resource("/v2.0/alarms/123")
|
||||
.resource("/v2.0/alarm-definitions/123")
|
||||
.header("X-Tenant-Id", "abc")
|
||||
.header("Content-Type", MediaType.APPLICATION_JSON)
|
||||
.put(
|
||||
ClientResponse.class,
|
||||
new UpdateAlarmCommand("Disk Exceeds 1k Operations", null, expression, "LOW",
|
||||
AlarmState.ALARM, true, alarmActions, null, null));
|
||||
new UpdateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null, expression,
|
||||
Arrays.asList("service", "instance_id"), "LOW", true, alarmActions, null, null));
|
||||
|
||||
assertEquals(response.getStatus(), 200);
|
||||
verify(service).update(eq("abc"), eq("123"), any(AlarmExpression.class),
|
||||
any(UpdateAlarmCommand.class));
|
||||
any(UpdateAlarmDefinitionCommand.class));
|
||||
}
|
||||
|
||||
public void shouldErrorOnCreateWithInvalidMetricName() {
|
||||
String expression = "avg(foo{service=hpcs.compute, instance_id=937}) >= 90";
|
||||
ClientResponse response =
|
||||
createResponseFor(new CreateAlarmCommand("Disk Exceeds 1k Operations", null, expression,
|
||||
"LOW", alarmActions, null, null));
|
||||
createResponseFor(new CreateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null,
|
||||
expression, Arrays.asList("service", "instance_id"), "LOW", alarmActions, null, null));
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"foo is not a valid metric name for namespace hpcs.compute");
|
||||
@@ -144,8 +138,8 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
public void shouldErrorOnCreateWithInvalidDimensions() {
|
||||
String expression = "avg(disk_read_ops{service=hpcs.compute, instance_id=937, foo=bar}) >= 90";
|
||||
ClientResponse response =
|
||||
createResponseFor(new CreateAlarmCommand("Disk Exceeds 1k Operations", null, expression,
|
||||
"LOW", alarmActions, null, null));
|
||||
createResponseFor(new CreateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null,
|
||||
expression, Arrays.asList("service", "instance_id"), "LOW", alarmActions, null, null));
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"foo is not a valid dimension name for service hpcs.compute");
|
||||
@@ -155,8 +149,8 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
String expression =
|
||||
"avg(hpcs.compute{instance_id=937, instance_id=123, az=2, instance_uuid=abc123, metric_name=disk_read_ops}) >= 90";
|
||||
ClientResponse response =
|
||||
createResponseFor(new CreateAlarmCommand("Disk Exceeds 1k Operations", null, expression,
|
||||
"LOW", alarmActions, null, null));
|
||||
createResponseFor(new CreateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null,
|
||||
expression, Arrays.asList("service", "instance_id"), "LOW", alarmActions, null, null));
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"The alarm expression is invalid",
|
||||
@@ -169,10 +163,10 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
when(
|
||||
service.create(eq("abc"), eq("Disk Exceeds 1k Operations"), any(String.class), eq("LOW"),
|
||||
eq(expression), eq(AlarmExpression.of(expression)), any(List.class), any(List.class),
|
||||
any(List.class))).thenReturn(alarm);
|
||||
any(List.class), any(List.class))).thenReturn(alarm);
|
||||
ClientResponse response =
|
||||
createResponseFor(new CreateAlarmCommand("Disk Exceeds 1k Operations", null, expression,
|
||||
"LOW", alarmActions, null, null));
|
||||
createResponseFor(new CreateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null,
|
||||
expression, Arrays.asList("service", "instance_id"), "LOW", alarmActions, null, null));
|
||||
assertEquals(response.getStatus(), 201);
|
||||
}
|
||||
|
||||
@@ -188,8 +182,8 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
String expression =
|
||||
"avg(hpcs.compute{instance_id=937, az=2, instance_uuid=0ff588fc-d298-482f-bb11-4b52d56801a4, metric_name=disk_read_ops}) & 90";
|
||||
ClientResponse response =
|
||||
createResponseFor(new CreateAlarmCommand("Disk Exceeds 1k Operations", null, expression,
|
||||
"LOW", alarmActions, null, null));
|
||||
createResponseFor(new CreateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null,
|
||||
expression, Arrays.asList("service", "instance_id"), "LOW", alarmActions, null, null));
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"The alarm expression is invalid", "Syntax Error");
|
||||
@@ -199,8 +193,8 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
String expression =
|
||||
"avg(hpcs.compute{instance_id=937, az=2, instance_uuid=0ff588fc-d298-482f-bb11-4b52d56801a4, metric_name=disk_read_ops},0) >= 90";
|
||||
ClientResponse response =
|
||||
createResponseFor(new CreateAlarmCommand("Disk Exceeds 1k Operations", null, expression,
|
||||
"LOW", alarmActions, null, null));
|
||||
createResponseFor(new CreateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null,
|
||||
expression, Arrays.asList("service", "instance_id"), "LOW", alarmActions, null, null));
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"Period must not be 0");
|
||||
@@ -210,8 +204,8 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
String expression =
|
||||
"avg(hpcs.compute{instance_id=937, az=2, instance_uuid=0ff588fc-d298-482f-bb11-4b52d56801a4, metric_name=disk_read_ops},61) >= 90";
|
||||
ClientResponse response =
|
||||
createResponseFor(new CreateAlarmCommand("Disk Exceeds 1k Operations", null, expression,
|
||||
"LOW", alarmActions, null, null));
|
||||
createResponseFor(new CreateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null,
|
||||
expression, Arrays.asList("service", "instance_id"), "LOW", alarmActions, null, null));
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"Period 61 must be a multiple of 60");
|
||||
@@ -221,8 +215,8 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
String expression =
|
||||
"avg(hpcs.compute{instance_id=937, az=2, instance_uuid=0ff588fc-d298-482f-bb11-4b52d56801a4, metric_name=disk_read_ops}) >= 90 times 0";
|
||||
ClientResponse response =
|
||||
createResponseFor(new CreateAlarmCommand("Disk Exceeds 1k Operations", null, expression,
|
||||
"LOW", alarmActions, null, null));
|
||||
createResponseFor(new CreateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null,
|
||||
expression, Arrays.asList("service", "instance_id"), "LOW", alarmActions, null, null));
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"Periods 0 must be greater than or equal to 1");
|
||||
@@ -232,8 +226,8 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
String expression =
|
||||
"avg(hpcs.compute{instance_id=937, az=2, instance_uuid=0ff588fc-d298-482f-bb11-4b52d56801a4, metric_name=disk_read_ops},60) >= 90 times 20161";
|
||||
ClientResponse response =
|
||||
createResponseFor(new CreateAlarmCommand("Disk Exceeds 1k Operations", null, expression,
|
||||
"LOW", alarmActions, null, null));
|
||||
createResponseFor(new CreateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null,
|
||||
expression, Arrays.asList("service", "instance_id"), "LOW", alarmActions, null, null));
|
||||
|
||||
ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422,
|
||||
"Period 60 times 20161 must total less than 2 weeks in seconds (1209600)");
|
||||
@@ -243,11 +237,12 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
String expression =
|
||||
"avg(hpcs.compute{instance_id=937, az=2, instance_uuid=0ff588fc-d298-482f-bb11-4b52d56801a4, metric_name=disk_read_ops}) >= 90";
|
||||
ClientResponse response =
|
||||
createResponseFor(new CreateAlarmCommand(
|
||||
createResponseFor(new CreateAlarmDefinitionCommand(
|
||||
"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789",
|
||||
null, "LOW", expression, alarmActions, null, null));
|
||||
null, expression, Arrays.asList("service", "instance_id"), "LOW", alarmActions, null,
|
||||
null));
|
||||
|
||||
ErrorMessages
|
||||
.assertThat(response.getEntity(String.class))
|
||||
@@ -261,8 +256,8 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
alarmActions = new ArrayList<String>();
|
||||
alarmActions.add("012345678901234567890123456789012345678901234567890");
|
||||
ClientResponse response =
|
||||
createResponseFor(new CreateAlarmCommand("Disk Exceeds 1k Operations", null, "LOW",
|
||||
expression, alarmActions, null, null));
|
||||
createResponseFor(new CreateAlarmDefinitionCommand("Disk Exceeds 1k Operations", null,
|
||||
expression, Arrays.asList("service", "instance_id"), "LOW", alarmActions, null, null));
|
||||
|
||||
ErrorMessages
|
||||
.assertThat(response.getEntity(String.class))
|
||||
@@ -272,28 +267,28 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldList() {
|
||||
List<Alarm> alarms =
|
||||
client().resource("/v2.0/alarms").header("X-Tenant-Id", "abc")
|
||||
.get(new GenericType<List<Alarm>>() {});
|
||||
List<AlarmDefinition> alarms =
|
||||
client().resource("/v2.0/alarm-definitions").header("X-Tenant-Id", "abc")
|
||||
.get(new GenericType<List<AlarmDefinition>>() {});
|
||||
|
||||
assertEquals(alarms, Arrays.asList(alarmItem));
|
||||
verify(repo).find(eq("abc"), anyString(), (Map<String, String>) anyMap(), any(String.class));
|
||||
verify(repo).find(eq("abc"), anyString(), (Map<String, String>) anyMap());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldListByName() throws Exception {
|
||||
List<Alarm> alarms =
|
||||
client().resource("/v2.0/alarms?name=" + URLEncoder.encode("foo bar baz", "UTF-8"))
|
||||
.header("X-Tenant-Id", "abc").get(new GenericType<List<Alarm>>() {});
|
||||
List<AlarmDefinition> alarms =
|
||||
client().resource("/v2.0/alarm-definitions?name=" + URLEncoder.encode("foo bar baz", "UTF-8"))
|
||||
.header("X-Tenant-Id", "abc").get(new GenericType<List<AlarmDefinition>>() {});
|
||||
|
||||
assertEquals(alarms, Arrays.asList(alarmItem));
|
||||
verify(repo).find(eq("abc"), eq("foo bar baz"), (Map<String, String>) anyMap(),
|
||||
any(String.class));
|
||||
verify(repo).find(eq("abc"), eq("foo bar baz"), (Map<String, String>) anyMap());
|
||||
}
|
||||
|
||||
public void shouldGet() {
|
||||
assertEquals(client().resource("/v2.0/alarms/123").header("X-Tenant-Id", "abc")
|
||||
.get(Alarm.class), alarm);
|
||||
assertEquals(
|
||||
client().resource("/v2.0/alarm-definitions/123").header("X-Tenant-Id", "abc")
|
||||
.get(AlarmDefinition.class), alarm);
|
||||
verify(repo).findById(eq("abc"), eq("123"));
|
||||
}
|
||||
|
||||
@@ -301,7 +296,7 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
doThrow(new EntityNotFoundException(null)).when(repo).findById(eq("abc"), eq("999"));
|
||||
|
||||
try {
|
||||
client().resource("/v2.0/alarms/999").header("X-Tenant-Id", "abc").get(Alarm.class);
|
||||
client().resource("/v2.0/alarm-definitions/999").header("X-Tenant-Id", "abc").get(AlarmDefinition.class);
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
assertTrue(e.getMessage().contains("404"));
|
||||
@@ -310,7 +305,7 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
|
||||
public void shouldDelete() {
|
||||
ClientResponse response =
|
||||
client().resource("/v2.0/alarms/123").header("X-Tenant-Id", "abc")
|
||||
client().resource("/v2.0/alarm-definitions/123").header("X-Tenant-Id", "abc")
|
||||
.delete(ClientResponse.class);
|
||||
assertEquals(response.getStatus(), 204);
|
||||
verify(service).delete(eq("abc"), eq("123"));
|
||||
@@ -320,7 +315,7 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
doThrow(new EntityNotFoundException(null)).when(service).delete(eq("abc"), eq("999"));
|
||||
|
||||
try {
|
||||
client().resource("/v2.0/alarms/999").header("X-Tenant-Id", "abc").delete();
|
||||
client().resource("/v2.0/alarm-definitions/999").header("X-Tenant-Id", "abc").delete();
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
assertTrue(e.getMessage().contains("404"));
|
||||
@@ -330,10 +325,10 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void should500OnInternalException() {
|
||||
doThrow(new RuntimeException("")).when(repo).find(anyString(), anyString(),
|
||||
(Map<String, String>) anyObject(), (String) anyObject());
|
||||
(Map<String, String>) anyObject());
|
||||
|
||||
try {
|
||||
client().resource("/v2.0/alarms").header("X-Tenant-Id", "abc").get(List.class);
|
||||
client().resource("/v2.0/alarm-definitions").header("X-Tenant-Id", "abc").get(List.class);
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
assertTrue(e.getMessage().contains("500"));
|
||||
@@ -342,39 +337,23 @@ public class AlarmResourceTest extends AbstractMonApiResourceTest {
|
||||
|
||||
public void shouldHydateLinksOnList() {
|
||||
List<Link> expected =
|
||||
Arrays.asList(new Link("self", "/v2.0/alarms/123"), new Link("history",
|
||||
"/v2.0/alarms/123/history"));
|
||||
Arrays.asList(new Link("self", "/v2.0/alarm-definitions/123"));
|
||||
List<Link> links =
|
||||
client().resource("/v2.0/alarms").header("X-Tenant-Id", "abc")
|
||||
.get(new GenericType<List<Alarm>>() {}).get(0).getLinks();
|
||||
client().resource("/v2.0/alarm-definitions").header("X-Tenant-Id", "abc")
|
||||
.get(new GenericType<List<AlarmDefinition>>() {}).get(0).getLinks();
|
||||
assertEquals(links, expected);
|
||||
}
|
||||
|
||||
public void shouldHydateLinksOnGet() {
|
||||
List<Link> links =
|
||||
Arrays.asList(new Link("self", "/v2.0/alarms/123"), new Link("history",
|
||||
"/v2.0/alarms/123/history"));
|
||||
assertEquals(client().resource("/v2.0/alarms/123").header("X-Tenant-Id", "abc")
|
||||
.get(Alarm.class).getLinks(), links);
|
||||
}
|
||||
|
||||
public void shouldGetAlarmStateHistory() throws Exception {
|
||||
AlarmStateHistory history1 =
|
||||
new AlarmStateHistory("123", AlarmState.OK, AlarmState.ALARM, "foo", "foobar",
|
||||
new DateTime(2014, 1, 1, 1, 1, DateTimeZone.UTC));
|
||||
AlarmStateHistory history2 =
|
||||
new AlarmStateHistory("123", AlarmState.ALARM, AlarmState.OK, "foo", "foobar",
|
||||
new DateTime(2014, 1, 1, 1, 1, DateTimeZone.UTC));
|
||||
List<AlarmStateHistory> expected = Arrays.asList(history1, history2);
|
||||
|
||||
when(stateHistoryRepo.findById(eq("abc"), eq("123"))).thenReturn(expected);
|
||||
|
||||
assertEquals(client().resource("/v2.0/alarms/123/state-history").header("X-Tenant-Id", "abc")
|
||||
.get(new GenericType<List<AlarmStateHistory>>() {}), expected);
|
||||
Arrays.asList(new Link("self", "/v2.0/alarm-definitions/123"));
|
||||
assertEquals(
|
||||
client().resource("/v2.0/alarm-definitions/123").header("X-Tenant-Id", "abc")
|
||||
.get(AlarmDefinition.class).getLinks(), links);
|
||||
}
|
||||
|
||||
private ClientResponse createResponseFor(Object request) {
|
||||
return client().resource("/v2.0/alarms").header("X-Tenant-Id", "abc")
|
||||
return client().resource("/v2.0/alarm-definitions").header("X-Tenant-Id", "abc")
|
||||
.header("Content-Type", MediaType.APPLICATION_JSON).post(ClientResponse.class, request);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,21 @@
|
||||
CREATE TABLE `alarm` (
|
||||
`id` varchar(36) NOT NULL,
|
||||
`alarm_definition_id` varchar(36) NOT NULL DEFAULT '',
|
||||
`state` varchar(20) NOT NULL check state in ('UNDETERMINED','OK','ALARM'),
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `tenant_id` (`alarm_definition_id`)
|
||||
);
|
||||
|
||||
CREATE TABLE `alarm_definition` (
|
||||
`id` varchar(36) NOT NULL,
|
||||
`tenant_id` varchar(36) NOT NULL,
|
||||
`name` varchar(250) DEFAULT NULL,
|
||||
`description` varchar(250) DEFAULT NULL,
|
||||
`expression` mediumtext,
|
||||
`severity` varchar(20) NOT NULL check severity in ('LOW','MEDIUM','HIGH','CRITICAL'),
|
||||
`state` varchar(20) NOT NULL check state in ('UNDETERMINED','OK','ALARM'),
|
||||
`match_by` varchar(255) DEFAULT '',
|
||||
`actions_enabled` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` datetime NOT NULL,
|
||||
@@ -13,19 +23,46 @@ CREATE TABLE `alarm` (
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
CREATE TABLE `alarm_metric` (
|
||||
`alarm_id` varchar(36) NOT NULL,
|
||||
`metric_definition_dimensions_id` binary(20) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
|
||||
PRIMARY KEY (`alarm_id`,`metric_definition_dimensions_id`)
|
||||
);
|
||||
|
||||
CREATE TABLE `metric_definition` (
|
||||
`id` binary(20) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
|
||||
`name` varchar(255) NOT NULL,
|
||||
`tenant_id` varchar(36) NOT NULL,
|
||||
`region` varchar(255) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
CREATE TABLE `metric_definition_dimensions` (
|
||||
`id` binary(20) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
|
||||
`metric_definition_id` binary(20) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
|
||||
`metric_dimension_set_id` binary(20) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
CREATE TABLE `metric_dimension` (
|
||||
`dimension_set_id` binary(20) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
|
||||
`name` varchar(255) NOT NULL DEFAULT '',
|
||||
`value` varchar(255) NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE TABLE `sub_alarm` (
|
||||
`id` varchar(36) NOT NULL,
|
||||
`alarm_id` varchar(36) NOT NULL,
|
||||
`alarm_definition_id` varchar(36) NOT NULL DEFAULT '',
|
||||
`function` varchar(10) NOT NULL,
|
||||
`metric_name` varchar(100) DEFAULT NULL,
|
||||
`operator` varchar(5) NOT NULL,
|
||||
`threshold` double NOT NULL,
|
||||
`period` int(11) NOT NULL,
|
||||
`periods` int(11) NOT NULL,
|
||||
`state` varchar(20) NOT NULL,
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `fk_sub_alarm` (`alarm_definition_id`)
|
||||
);
|
||||
|
||||
CREATE TABLE `sub_alarm_dimension` (
|
||||
@@ -39,5 +76,5 @@ CREATE TABLE `alarm_action` (
|
||||
`alarm_id` varchar(36) NOT NULL,
|
||||
`alarm_state` varchar(20) NOT NULL check alarm_state in ('UNDETERMINED','OK','ALARM'),
|
||||
`action_id` varchar(36) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (`alarm_id`,`alarm_state`,`action_id`),
|
||||
PRIMARY KEY (`alarm_id`,`alarm_state`,`action_id`)
|
||||
);
|
||||
@@ -1 +1 @@
|
||||
{"id":"123","links":[{"rel":"self","href":"https://region-a.geo-1.maas.hpcloudsvc.com/v1.0"}],"name":"90% CPU","description":"","expression":"avg(hpcs.compute{instance_id=666, image_id=345}) >= 90","expression_data":{"function":"AVG","metric_name":"hpcs.compute","dimensions":{"image_id":"345","instance_id":"666"},"operator":"GTE","threshold":90.0,"period":60,"periods":1},"state":"OK","severity":"LOW","actions_enabled":false,"alarm_actions":["123345345","23423"],"ok_actions":null,"undetermined_actions":null}
|
||||
{"id":"123","links":[{"rel":"self","href":"https://region-a.geo-1.maas.hpcloudsvc.com/v1.0"}],"name":"90% CPU","description":"","expression":"avg(hpcs.compute{instance_id=666, image_id=345}) >= 90","match_by":[],"severity":"LOW","actions_enabled":false,"alarm_actions":["123345345","23423"],"ok_actions":null,"undetermined_actions":null}
|
||||
@@ -24,7 +24,7 @@ kafka:
|
||||
|
||||
mysql:
|
||||
driverClass: com.mysql.jdbc.Driver
|
||||
url: jdbc:mysql://192.168.59.103:3306/mon?connectTimeout=5000&autoReconnect=true
|
||||
url: jdbc:mysql://localhost:3306/mon?connectTimeout=5000&autoReconnect=true
|
||||
user: monapi
|
||||
password: password
|
||||
maxWaitForConnection: 1s
|
||||
@@ -34,16 +34,16 @@ mysql:
|
||||
checkConnectionWhileIdle: false
|
||||
checkConnectionOnBorrow: true
|
||||
|
||||
vertica:
|
||||
driverClass: com.vertica.jdbc.Driver
|
||||
url: jdbc:vertica://192.168.10.4/mon
|
||||
user: mon_api
|
||||
password: password
|
||||
maxWaitForConnection: 1s
|
||||
validationQuery: "/* MyService Health Check */ SELECT 1"
|
||||
minSize: 4
|
||||
maxSize: 32
|
||||
checkConnectionWhileIdle: false
|
||||
# vertica:
|
||||
# driverClass: com.vertica.jdbc.Driver
|
||||
# url: jdbc:vertica://192.168.10.4/mon
|
||||
# user: mon_api
|
||||
# password: password
|
||||
# maxWaitForConnection: 1s
|
||||
# validationQuery: "/* MyService Health Check */ SELECT 1"
|
||||
# minSize: 4
|
||||
# maxSize: 32
|
||||
# checkConnectionWhileIdle: false
|
||||
|
||||
|
||||
influxDB:
|
||||
|
||||
Reference in New Issue
Block a user