Removing java API

Moving python package from monasca to monasca_events_api
This commit is contained in:
Joe Keen 2015-04-27 14:58:12 -06:00
parent 7c07276fc8
commit 1017120c9d
271 changed files with 0 additions and 15886 deletions

View File

@ -1,2 +0,0 @@
[python: **.py]

View File

@ -1,29 +0,0 @@
#!/bin/sh
set -x
ME=`whoami`
echo "Running as user: $ME"
MVN=$1
VERSION=$2
check_user() {
ME=$1
if [ "${ME}" != "jenkins" ]; then
echo "\nERROR: Download monasca-common and do a mvn install to install the monasca-commom jars\n" 1>&2
exit 1
fi
}
BUILD_COMMON=false
POM_FILE=~/.m2/repository/monasca-common/monasca-common/${VERSION}/monasca-common-${VERSION}.pom
if [ ! -r "${POM_FILE}" ]; then
check_user ${ME}
BUILD_COMMON=true
fi
# This should only be done on the stack forge system
if [ "${BUILD_COMMON}" = "true" ]; then
git clone https://github.com/stackforge/monasca-common
cd monasca-common
${MVN} clean
${MVN} install
fi

View File

@ -1,422 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>monasca-api</groupId>
<artifactId>monasca-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
<url>http://github.com/stackforge/monasca-api</url>
<packaging>jar</packaging>
<prerequisites>
<maven>3.0</maven>
</prerequisites>
<properties>
<gitRevision></gitRevision>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss</maven.build.timestamp.format>
<computedVersion>${project.version}-${timestamp}-${gitRevision}</computedVersion>
<computedName>${project.artifactId}-${computedVersion}</computedName>
<mon.common.version>1.0.0-SNAPSHOT</mon.common.version>
<dropwizard.version>0.7.0</dropwizard.version>
<skipITs>false</skipITs>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<shadedJarName>${project.artifactId}-${project.version}-shaded
</shadedJarName>
</properties>
<scm>
<connection>scm:git:git@github.com:stackforge/monasca-api</connection>
<developerConnection>scm:git:git@github.com:stackforge/monasca-api</developerConnection>
</scm>
<profiles>
<profile>
<id>release-deploy-url-override</id>
<activation>
<property>
<name>BUILD_NUM</name>
</property>
</activation>
<properties>
<computedVersion>${versionNumber}.${BUILD_NUM}</computedVersion>
</properties>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>monasca-common</groupId>
<artifactId>monasca-common-influxdb</artifactId>
<version>${mon.common.version}</version>
</dependency>
<dependency>
<groupId>monasca-common</groupId>
<artifactId>monasca-common-model</artifactId>
<version>${mon.common.version}</version>
</dependency>
<dependency>
<groupId>monasca-common</groupId>
<artifactId>monasca-common-persistence</artifactId>
<version>${mon.common.version}</version>
</dependency>
<dependency>
<groupId>monasca-common</groupId>
<artifactId>monasca-common-util</artifactId>
<version>${mon.common.version}</version>
</dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>monasca-common</groupId>
<artifactId>monasca-common-kafka</artifactId>
<version>${mon.common.version}</version>
</dependency>
<dependency>
<groupId>monasca-common</groupId>
<artifactId>monasca-common-middleware</artifactId>
<version>${mon.common.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-db</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-jdbi</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-assets</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-jersey</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.26</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.2.0-incubating</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.9.2</artifactId>
<version>0.8.0</version>
<exclusions>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.influxdb</groupId>
<artifactId>influxdb-java</artifactId>
<version>1.0</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>monasca-common</groupId>
<artifactId>monasca-common-testing</artifactId>
<version>${mon.common.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>monasca-common</groupId>
<artifactId>monasca-common-dropwizard</artifactId>
<version>${mon.common.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-testing</artifactId>
<version>${dropwizard.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.restassured</groupId>
<artifactId>rest-assured</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.8.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.5</version>
<configuration>
<filesets>
<fileset>
<directory>${project.basedir}/debs</directory>
</fileset>
</filesets>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
<configuration>
<doCheck>false</doCheck>
<shortRevisionLength>6</shortRevisionLength>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-testng</artifactId>
<version>2.17</version>
</dependency>
</dependencies>
<configuration>
<excludedGroups>performance,functional,integration,database,slow
</excludedGroups>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.17</version>
<configuration>
<groups>performance,functional,integration,database,slow</groups>
<skipTests>${skipITs}</skipTests>
<parallel>methods</parallel>
<threadCount>4</threadCount>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.2</version>
<configuration>
<finalName>${computedName}</finalName>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<!-- *:* can't be used for artifact because we are using an older shade plugin -->
<artifact>org.eclipse.jetty.orbit:javax.servlet</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<artifactSet>
<excludes>
<exclude>org.hamcrest:hamcrest-core</exclude>
<exclude>org.hamcrest:hamcrest-library</exclude>
</excludes>
</artifactSet>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>monasca.api.MonApiApplication</mainClass>
</transformer>
</transformers>
<shadedArtifactAttached>true</shadedArtifactAttached>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<packageName>monasca.api</packageName>
</manifest>
<manifestEntries>
<Implementation-Version>${project.artifactId}-${computedVersion}</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<descriptors>
<descriptor>src/assembly/tar.xml</descriptor>
</descriptors>
<finalName>${artifactNamedVersion}</finalName>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>jdeb</artifactId>
<groupId>org.vafer</groupId>
<version>1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jdeb</goal>
</goals>
<configuration>
<deb>${project.basedir}/debs/binaries/${computedName}.deb</deb>
<dataSet>
<data>
<type>file</type>
<src>${project.build.directory}/${shadedJarName}.jar</src>
<dst>/opt/monasca/monasca-api.jar</dst>
</data>
<data>
<type>file</type>
<src>${project.basedir}/src/deb/etc/api-config.yml-sample
</src>
<dst>/etc/monasca/api-config.yml-sample</dst>
</data>
</dataSet>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-scm-plugin</artifactId>
<version>1.9.2</version>
<configuration>
<tag>${project.version}</tag>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,29 +0,0 @@
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>tar</id>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>README*</include>
<include>LICENSE*</include>
</includes>
</fileSet>
</fileSets>
<files>
<file>
<source>${project.build.directory}/${shadedJarName}.jar</source>
<outputDirectory>/</outputDirectory>
<destName>monasca-api.jar</destName>
</file>
<file>
<source>${project.basedir}/src/deb/etc/api-config.yml-sample</source>
<outputDirectory>examples</outputDirectory>
</file>
</files>
</assembly>

View File

@ -1,9 +0,0 @@
Package: [[name]]
Section: misc
Priority: optional
Architecture: all
Depends: openjdk-7-jre-headless | openjdk-7-jre
Version: [[version]]-[[timestamp]]-[[buildNumber]]
Maintainer: Monasca Team <monasca@lists.launchpad.net>
Description: Monasca-API
RESTful API for all Monasca data.

View File

@ -1,9 +0,0 @@
#!/bin/sh
case "$1" in
remove)
stop monasca-api
;;
esac
exit 0

View File

@ -1,135 +0,0 @@
# The region for which all metrics passing through this server will be persisted
region: region-a
# Maximum rows (Mysql) or points (Influxdb) to return when listing elements
maxQueryLimit: 10000
# Whether this server is running on a secure port
accessedViaHttps: false
# Topic for publishing metrics to
metricsTopic: metrics
# Topic for publishing domain events to
eventsTopic: events
kafka:
brokerUris:
- 192.168.10.4:9092
zookeeperUris:
- 192.168.10.4:2181
healthCheckTopic: healthcheck
mysql:
driverClass: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.10.4:3306/mon?connectTimeout=5000&autoReconnect=true&useLegacyDatetimeCode=false
user: monapi
password: password
maxWaitForConnection: 1s
validationQuery: "/* MyService Health Check */ SELECT 1"
minSize: 8
maxSize: 32
checkConnectionWhileIdle: false
checkConnectionOnBorrow: true
databaseConfiguration:
databaseType: influxdb
influxDB:
version: V9
maxHttpConnections: 100
# Retention policy may be left blank to indicate default policy.
retentionPolicy:
name: mon
url: http://192.168.10.4:8086
user: mon_api
password: password
vertica:
driverClass: com.vertica.jdbc.Driver
url: jdbc:vertica://192.168.10.8/mon
user: dbadmin
password: password
maxWaitForConnection: 1s
validationQuery: "/* MyService Health Check */ SELECT 1"
minSize: 4
maxSize: 32
checkConnectionWhileIdle: false
middleware:
enabled: true
serverVIP: identity.example.com
serverPort: 9543
useHttps: true
truststore: etc/monasca/truststore.jks
truststorePass: changeit
connTimeout: 500
connSSLClientAuth: true
keystore: etc/monasca/keystore.jks
keystorePass: changeit
connPoolMaxActive: 3
connPoolMaxIdle: 3
connPoolEvictPeriod: 600000
connPoolMinIdleTime: 600000
connRetryTimes: 2
connRetryInterval: 50
defaultAuthorizedRoles: [user, domainuser, domainadmin, monasca-user]
agentAuthorizedRoles: [monasca-agent]
adminAuthMethod: password
adminUser: admin
adminPassword: admin
adminProjectId:
adminProjectName:
adminToken:
timeToCacheToken: 600
maxTokenCacheSize: 1048576
server:
applicationConnectors:
- type: http
port: 8080
maxRequestHeaderSize: 16KiB # Allow large headers used by keystone tokens
requestLog:
timeZone: UTC
appenders:
- type: file
currentLogFilename: /var/log/monasca/api/request.log
threshold: ALL
archive: true
archivedLogFilenamePattern: /var/log/monasca/api/request-%d.log.gz
archivedFileCount: 5
# Logging settings.
logging:
# The default level of all loggers. Can be OFF, ERROR, WARN, INFO, DEBUG, TRACE, or ALL.
level: WARN
# Logger-specific levels.
loggers:
# Sets the level for 'com.example.app' to DEBUG.
com.example.app: DEBUG
appenders:
- type: console
threshold: ALL
timeZone: UTC
target: stdout
logFormat: # TODO
- type: file
currentLogFilename: /var/log/monasca/api/monasca-api.log
threshold: ALL
archive: true
archivedLogFilenamePattern: /var/log/monasca/api/monasca-api-%d.log.gz
archivedFileCount: 5
timeZone: UTC
logFormat: # TODO
- type: syslog
host: localhost
port: 514
facility: local0
threshold: ALL
logFormat: # TODO

View File

@ -1,63 +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 monasca.api;
import com.fasterxml.jackson.annotation.JsonProperty;
import monasca.common.messaging.kafka.KafkaConfiguration;
import monasca.api.infrastructure.middleware.MiddlewareConfiguration;
import monasca.common.configuration.DatabaseConfiguration;
import monasca.common.configuration.InfluxDbConfiguration;
import io.dropwizard.Configuration;
import io.dropwizard.db.DataSourceFactory;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
public class ApiConfig extends Configuration {
@NotEmpty
public String region;
@NotNull
public Boolean accessedViaHttps;
@NotEmpty
public String metricsTopic = "metrics";
@NotEmpty
public String eventsTopic = "events";
@NotNull
public int maxQueryLimit;
@NotEmpty
public String alarmStateTransitionsTopic = "alarm-state-transitions";
@Valid
@NotNull
public DataSourceFactory mysql;
@Valid
@NotNull
public DataSourceFactory vertica;
@Valid
@NotNull
public KafkaConfiguration kafka;
@Valid
@NotNull
public MiddlewareConfiguration middleware;
@Valid
public InfluxDbConfiguration influxDB;
@Valid
@JsonProperty
public DatabaseConfiguration databaseConfiguration;
}

View File

@ -1,245 +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 monasca.api;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.eclipse.jetty.servlets.CrossOriginFilter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.servlet.FilterRegistration.Dynamic;
import javax.ws.rs.ext.ExceptionMapper;
import io.dropwizard.Application;
import io.dropwizard.jdbi.bundles.DBIExceptionsBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import monasca.api.infrastructure.servlet.MockAuthenticationFilter;
import monasca.api.infrastructure.servlet.PostAuthenticationFilter;
import monasca.api.infrastructure.servlet.PreAuthenticationFilter;
import monasca.api.infrastructure.servlet.RoleAuthorizationFilter;
import monasca.api.resource.AlarmDefinitionResource;
import monasca.api.resource.AlarmResource;
import monasca.api.resource.MeasurementResource;
import monasca.api.resource.MetricResource;
import monasca.api.resource.NotificationMethodResource;
import monasca.api.resource.StatisticResource;
import monasca.api.resource.VersionResource;
import monasca.api.resource.exception.ConstraintViolationExceptionMapper;
import monasca.api.resource.exception.EntityExistsExceptionMapper;
import monasca.api.resource.exception.EntityNotFoundExceptionMapper;
import monasca.api.resource.exception.IllegalArgumentExceptionMapper;
import monasca.api.resource.exception.InvalidEntityExceptionMapper;
import monasca.api.resource.exception.JsonMappingExceptionManager;
import monasca.api.resource.exception.JsonProcessingExceptionMapper;
import monasca.api.resource.exception.MultipleMetricsExceptionMapper;
import monasca.api.resource.exception.ThrowableExceptionMapper;
import monasca.api.resource.serialization.SubAlarmExpressionSerializer;
import monasca.common.middleware.AuthConstants;
import monasca.common.middleware.TokenAuth;
import monasca.common.util.Injector;
/**
* Monitoring API application.
*/
public class MonApiApplication extends Application<ApiConfig> {
public static void main(String[] args) throws Exception {
/*
* This should allow command line options to show the current version
* java -jar monasca-api.jar --version
* java -jar monasca-api.jar -version
* java -jar monasca-api.jar version
* Really anything with the word version in it will show the
* version as long as there is only one argument
* */
if (args.length == 1 && args[0].toLowerCase().contains("version")) {
showVersion();
System.exit(0);
}
new MonApiApplication().run(args);
}
private static void showVersion() {
Package pkg;
pkg = Package.getPackage("monasca.api");
System.out.println("-------- Version Information --------");
System.out.println(pkg.getImplementationVersion());
}
@Override
public void initialize(Bootstrap<ApiConfig> bootstrap) {
/** Configure bundles */
bootstrap.addBundle(new DBIExceptionsBundle());
}
@Override
public String getName() {
return "HP Cloud Monitoring";
}
@Override
@SuppressWarnings("unchecked")
public void run(ApiConfig 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));
environment.jersey().register(Injector.getInstance(StatisticResource.class));
environment.jersey().register(Injector.getInstance(NotificationMethodResource.class));
/** Configure providers */
removeExceptionMappers(environment.jersey().getResourceConfig().getSingletons());
environment.jersey().register(new EntityExistsExceptionMapper());
environment.jersey().register(new EntityNotFoundExceptionMapper());
environment.jersey().register(new IllegalArgumentExceptionMapper());
environment.jersey().register(new InvalidEntityExceptionMapper());
environment.jersey().register(new JsonProcessingExceptionMapper());
environment.jersey().register(new JsonMappingExceptionManager());
environment.jersey().register(new ConstraintViolationExceptionMapper());
environment.jersey().register(new ThrowableExceptionMapper<Throwable>() {});
environment.jersey().register(new MultipleMetricsExceptionMapper());
/** Configure Jackson */
environment.getObjectMapper().setPropertyNamingStrategy(
PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
environment.getObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
environment.getObjectMapper().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
SimpleModule module = new SimpleModule("SerializationModule");
module.addSerializer(new SubAlarmExpressionSerializer());
environment.getObjectMapper().registerModule(module);
/** Configure CORS filter */
Dynamic corsFilter = environment.servlets().addFilter("cors", CrossOriginFilter.class);
corsFilter.addMappingForUrlPatterns(null, true, "/*");
corsFilter.setInitParameter("allowedOrigins", "*");
corsFilter.setInitParameter("allowedHeaders",
"X-Requested-With,Content-Type,Accept,Origin,X-Auth-Token");
corsFilter.setInitParameter("allowedMethods", "OPTIONS,GET,HEAD");
if (config.middleware.enabled) {
ensureHasValue(config.middleware.serverVIP, "serverVIP", "enabled", "true");
ensureHasValue(config.middleware.serverPort, "serverPort", "enabled", "true");
ensureHasValue(config.middleware.adminAuthMethod, "adminAuthMethod", "enabled", "true");
if ("password".equalsIgnoreCase(config.middleware.adminAuthMethod)) {
ensureHasValue(config.middleware.adminUser, "adminUser", "adminAuthMethod", "password");
ensureHasValue(config.middleware.adminPassword, "adminPassword", "adminAuthMethod", "password");
} else if ("token".equalsIgnoreCase(config.middleware.adminAuthMethod)) {
ensureHasValue(config.middleware.adminToken, "adminToken", "adminAuthMethod", "token");
} else {
throw new Exception(String.format(
"Invalid value '%s' for adminAuthMethod. Must be either password or token",
config.middleware.adminAuthMethod));
}
if (config.middleware.defaultAuthorizedRoles == null || config.middleware.defaultAuthorizedRoles.isEmpty()) {
ensureHasValue(null, "defaultAuthorizedRoles", "enabled", "true");
}
if (config.middleware.connSSLClientAuth) {
ensureHasValue(config.middleware.keystore, "keystore", "connSSLClientAuth", "true");
ensureHasValue(config.middleware.keystorePassword, "keystorePassword", "connSSLClientAuth", "true");
}
Map<String, String> authInitParams = new HashMap<String, String>();
authInitParams.put("ServerVIP", config.middleware.serverVIP);
authInitParams.put("ServerPort", config.middleware.serverPort);
authInitParams.put(AuthConstants.USE_HTTPS, String.valueOf(config.middleware.useHttps));
authInitParams.put("ConnTimeout", config.middleware.connTimeout);
authInitParams.put("ConnSSLClientAuth", String.valueOf(config.middleware.connSSLClientAuth));
authInitParams.put("ConnPoolMaxActive", config.middleware.connPoolMaxActive);
authInitParams.put("ConnPoolMaxIdle", config.middleware.connPoolMaxActive);
authInitParams.put("ConnPoolEvictPeriod", config.middleware.connPoolEvictPeriod);
authInitParams.put("ConnPoolMinIdleTime", config.middleware.connPoolMinIdleTime);
authInitParams.put("ConnRetryTimes", config.middleware.connRetryTimes);
authInitParams.put("ConnRetryInterval", config.middleware.connRetryInterval);
authInitParams.put("AdminToken", config.middleware.adminToken);
authInitParams.put("TimeToCacheToken", config.middleware.timeToCacheToken);
authInitParams.put("AdminAuthMethod", config.middleware.adminAuthMethod);
authInitParams.put("AdminUser", config.middleware.adminUser);
authInitParams.put("AdminPassword", config.middleware.adminPassword);
authInitParams.put(AuthConstants.ADMIN_PROJECT_ID, config.middleware.adminProjectId);
authInitParams.put(AuthConstants.ADMIN_PROJECT_NAME, config.middleware.adminProjectName);
authInitParams.put("MaxTokenCacheSize", config.middleware.maxTokenCacheSize);
setIfNotNull(authInitParams, AuthConstants.TRUSTSTORE, config.middleware.truststore);
setIfNotNull(authInitParams, AuthConstants.TRUSTSTORE_PASS, config.middleware.truststorePassword);
setIfNotNull(authInitParams, AuthConstants.KEYSTORE, config.middleware.keystore);
setIfNotNull(authInitParams, AuthConstants.KEYSTORE_PASS, config.middleware.keystorePassword);
/** Configure auth filters */
Dynamic preAuthenticationFilter =
environment.servlets().addFilter("pre-auth", new PreAuthenticationFilter());
preAuthenticationFilter.addMappingForUrlPatterns(null, true, "/");
preAuthenticationFilter.addMappingForUrlPatterns(null, true, "/v2.0/*");
Dynamic tokenAuthFilter = environment.servlets().addFilter("token-auth", new TokenAuth());
tokenAuthFilter.addMappingForUrlPatterns(null, true, "/");
tokenAuthFilter.addMappingForUrlPatterns(null, true, "/v2.0/*");
tokenAuthFilter.setInitParameters(authInitParams);
Dynamic postAuthenticationFilter =
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());
} else {
Dynamic mockAuthenticationFilter =
environment.servlets().addFilter("mock-auth", new MockAuthenticationFilter());
mockAuthenticationFilter.addMappingForUrlPatterns(null, true, "/");
mockAuthenticationFilter.addMappingForUrlPatterns(null, true, "/v2.0/*");
}
}
private void ensureHasValue(final String value, final String what, final String control,
final String controlValue) throws Exception {
if (value == null || value.isEmpty()) {
final String message =
String
.format(
"Since %s in middleware section of configuration file is set to %s, %s must have a value",
control, controlValue, what);
throw new Exception(message);
}
}
private void setIfNotNull(Map<String, String> authInitParams, String name, String value) {
if (value != null) {
authInitParams.put(name, value);
}
}
private void removeExceptionMappers(Set<Object> items) {
for (Iterator<Object> i = items.iterator(); i.hasNext();) {
Object o = i.next();
if (o instanceof ExceptionMapper)
i.remove();
}
}
}

View File

@ -1,96 +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 monasca.api;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.jdbi.DBIFactory;
import io.dropwizard.setup.Environment;
import java.util.Properties;
import javax.inject.Named;
import javax.inject.Singleton;
import kafka.javaapi.producer.Producer;
import kafka.producer.ProducerConfig;
import org.skife.jdbi.v2.DBI;
import com.codahale.metrics.MetricRegistry;
import com.google.common.base.Joiner;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
import com.google.inject.name.Names;
import monasca.api.app.ApplicationModule;
import monasca.api.domain.DomainModule;
import monasca.api.infrastructure.InfrastructureModule;
/**
* Monitoring API server bindings.
*/
public class MonApiModule extends AbstractModule {
private final ApiConfig config;
private final Environment environment;
public MonApiModule(Environment environment, ApiConfig config) {
this.environment = environment;
this.config = config;
}
@Override
protected void configure() {
bind(ApiConfig.class).toInstance(config);
bind(MetricRegistry.class).toInstance(environment.metrics());
bind(DataSourceFactory.class).annotatedWith(Names.named("mysql")).toInstance(config.mysql);
bind(DataSourceFactory.class).annotatedWith(Names.named("vertica")).toInstance(config.vertica);
install(new ApplicationModule());
install(new DomainModule());
install(new InfrastructureModule(this.config));
}
@Provides
@Singleton
@Named("mysql")
public DBI getMySqlDBI() {
try {
return new DBIFactory().build(environment, config.mysql, "mysql");
} catch (ClassNotFoundException e) {
throw new ProvisionException("Failed to provision MySQL DBI", e);
}
}
@Provides
@Singleton
@Named("vertica")
public DBI getVerticaDBI() {
try {
return new DBIFactory().build(environment, config.vertica, "vertica");
} catch (ClassNotFoundException e) {
throw new ProvisionException("Failed to provision Vertica DBI", e);
}
}
@Provides
@Singleton
public Producer<String, String> getProducer() {
Properties props = new Properties();
props.put("metadata.broker.list", Joiner.on(',').join(config.kafka.brokerUris));
props.put("serializer.class", "kafka.serializer.StringEncoder");
props.put("request.required.acks", "1");
ProducerConfig config = new ProducerConfig(props);
return new Producer<String, String>(config);
}
}

View File

@ -1,416 +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 monasca.api.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 monasca.api.ApiConfig;
import monasca.api.app.command.UpdateAlarmDefinitionCommand;
import monasca.api.app.validation.DimensionValidation;
import monasca.common.model.event.AlarmDefinitionCreatedEvent;
import monasca.common.model.event.AlarmDefinitionDeletedEvent;
import monasca.common.model.event.AlarmDefinitionUpdatedEvent;
import monasca.common.model.event.AlarmDeletedEvent;
import monasca.common.model.alarm.AlarmExpression;
import monasca.common.model.alarm.AlarmSubExpression;
import monasca.common.model.metric.MetricDefinition;
import monasca.api.domain.exception.EntityExistsException;
import monasca.api.domain.exception.EntityNotFoundException;
import monasca.api.domain.exception.InvalidEntityException;
import monasca.api.domain.model.alarm.Alarm;
import monasca.api.domain.model.alarm.AlarmRepo;
import monasca.api.domain.model.alarmdefinition.AlarmDefinition;
import monasca.api.domain.model.alarmdefinition.AlarmDefinitionRepo;
import monasca.api.domain.model.notificationmethod.NotificationMethodRepo;
import monasca.common.util.Exceptions;
import monasca.common.util.Serialization;
/**
* Services alarm definition related requests.
*/
public class AlarmDefinitionService {
private static final Logger LOG = LoggerFactory.getLogger(AlarmService.class);
private final ApiConfig config;
private final Producer<String, String> producer;
private final AlarmDefinitionRepo repo;
private final AlarmRepo alarmRepo;
private final NotificationMethodRepo notificationMethodRepo;
long eventCount;
@Inject
public AlarmDefinitionService(ApiConfig config, Producer<String, String> producer,
AlarmDefinitionRepo repo, AlarmRepo alarmRepo,
NotificationMethodRepo notificationMethodRepo) {
this.config = config;
this.producer = producer;
this.repo = repo;
this.alarmRepo = alarmRepo;
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
String alarmDefID=repo.exists(tenantId, name);
if (alarmDefID!=null) {
throw new EntityExistsException(
"An alarm definition already exists for project / tenant: %s named: %s", tenantId, name);
}
DimensionValidation.validateNames(matchBy);
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, String.valueOf(eventCount++), 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);
// Have to get information about the Alarms before they are deleted. They will be deleted
// by the database as a cascade delete from the Alarm Definition delete
final List<Alarm> alarms = alarmRepo.find(tenantId, alarmDefId, null, null, null, null, null, 1, false);
final Map<String, Map<String, AlarmSubExpression>> alarmSubExpressions =
alarmRepo.findAlarmSubExpressionsForAlarmDefinition(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, String.valueOf(eventCount++), event));
// Notify about the Deletion of the Alarms second because that is the order that thresh
// wants it so Alarms don't get recreated
for (final Alarm alarm : alarms) {
String alarmDeletedEvent =
Serialization.toJson(new AlarmDeletedEvent(tenantId, alarm.getId(), alarm.getMetrics(),
alarmDefId, alarmSubExpressions.get(alarm.getId())));
producer.send(new KeyedMessage<>(config.eventsTopic, String.valueOf(eventCount++), alarmDeletedEvent));
}
}
/**
* 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) {
final AlarmDefinition oldAlarmDefinition = assertAlarmDefinitionExists(
tenantId, alarmDefId, command.alarmActions, command.okActions,
command.undeterminedActions);
final SubExpressions subExpressions = subExpressionsFor(
repo.findSubExpressions(alarmDefId), alarmExpression);
String alarmID = repo.exists(tenantId, command.name);
if (alarmID != null && !alarmID.equalsIgnoreCase(alarmDefId)) {
throw new EntityExistsException(
"An alarm definition with the same name already exists for project / tenant: %s named: %s",
tenantId, command.name);
}
validateChangesAllowed(command.matchBy, oldAlarmDefinition,
subExpressions);
updateInternal(tenantId, alarmDefId, false, command.name,
command.description, command.expression, command.matchBy,
command.severity, alarmExpression, command.actionsEnabled,
command.alarmActions, command.okActions,
command.undeterminedActions, subExpressions);
return new AlarmDefinition(alarmDefId, command.name,
command.description, command.severity, command.expression,
command.matchBy, command.actionsEnabled, command.alarmActions,
command.okActions, command.undeterminedActions);
}
/**
* Don't allow changes that would cause existing Alarms for this AlarmDefinition to be invalidated.
*
* matchBy can't change and the expression can't change the metrics used or number of subexpressions
*/
private void validateChangesAllowed(final List<String> newMatchBy,
final AlarmDefinition oldAlarmDefinition, final SubExpressions subExpressions) {
final boolean matchBySame;
if (oldAlarmDefinition.getMatchBy() == null || oldAlarmDefinition.getMatchBy().isEmpty()) {
matchBySame = newMatchBy == null || newMatchBy.isEmpty();
}
else {
matchBySame = oldAlarmDefinition.getMatchBy().equals(newMatchBy);
}
if (!matchBySame) {
throw monasca.api.resource.exception.Exceptions.unprocessableEntity("match_by must not change");
}
if (!subExpressions.oldAlarmSubExpressions.isEmpty() || !subExpressions.newAlarmSubExpressions.isEmpty()) {
final int newCount = subExpressions.newAlarmSubExpressions.size() +
subExpressions.changedSubExpressions.size() +
subExpressions.unchangedSubExpressions.size();
if (newCount != AlarmExpression.of(oldAlarmDefinition.getExpression()).getSubExpressions().size()) {
throw monasca.api.resource.exception.Exceptions.unprocessableEntity("number of subexpressions must not change");
}
else {
throw monasca.api.resource.exception.Exceptions.unprocessableEntity("metrics in subexpression must not change");
}
}
}
/**
* 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, Boolean enabled, List<String> alarmActions,
List<String> okActions, List<String> undeterminedActions) {
AlarmDefinition oldAlarmDefinition = assertAlarmDefinitionExists(
tenantId, alarmDefId, alarmActions, okActions,
undeterminedActions);
name = name == null ? oldAlarmDefinition.getName() : name;
String alarmID = repo.exists(tenantId, name);
if (alarmID != null && !alarmID.equalsIgnoreCase(alarmDefId)) {
throw new EntityExistsException(
"An alarm definition with the same name already exists for project / tenant: %s named: %s",
tenantId, name);
}
description = description == null ? oldAlarmDefinition.getDescription()
: description;
expression = expression == null ? oldAlarmDefinition.getExpression()
: expression;
severity = severity == null ? oldAlarmDefinition.getSeverity()
: severity;
alarmExpression = alarmExpression == null ? AlarmExpression
.of(expression) : alarmExpression;
enabled = enabled == null ? oldAlarmDefinition.isActionsEnabled()
: enabled;
matchBy = matchBy == null ? oldAlarmDefinition.getMatchBy() : matchBy;
final SubExpressions subExpressions = subExpressionsFor(
repo.findSubExpressions(alarmDefId), alarmExpression);
validateChangesAllowed(matchBy, oldAlarmDefinition, subExpressions);
updateInternal(tenantId, alarmDefId, true, name, description,
expression, matchBy, severity, alarmExpression, enabled,
alarmActions, okActions, undeterminedActions, subExpressions);
return new AlarmDefinition(alarmDefId, name, description, severity,
expression, matchBy, enabled,
alarmActions == null ? oldAlarmDefinition.getAlarmActions()
: alarmActions,
okActions == null ? oldAlarmDefinition.getOkActions()
: okActions,
undeterminedActions == null ? oldAlarmDefinition
.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) {
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, severity,
subExpressions.oldAlarmSubExpressions,
subExpressions.changedSubExpressions,
subExpressions.unchangedSubExpressions,
subExpressions.newAlarmSubExpressions));
producer.send(new KeyedMessage<>(config.eventsTopic, String
.valueOf(eventCount++), 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(final Map<String, AlarmSubExpression> initialSubExpressions,
AlarmExpression alarmExpression) {
BiMap<String, AlarmSubExpression> oldExpressions =
HashBiMap.create(initialSubExpressions);
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 metrics of {@code a} and {@code b} are the same. The Threshold
* Engine can handle any other type of change to the expression
*/
private boolean sameKeyFields(AlarmSubExpression a, AlarmSubExpression b) {
return a.getMetricDefinition().equals(b.getMetricDefinition());
}
/**
* 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);
}
}

View File

@ -1,141 +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 monasca.api.app;
import java.util.Map;
import javax.inject.Inject;
import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import monasca.api.ApiConfig;
import monasca.api.app.command.UpdateAlarmCommand;
import monasca.common.model.event.AlarmDeletedEvent;
import monasca.common.model.event.AlarmStateTransitionedEvent;
import monasca.common.model.event.AlarmUpdatedEvent;
import monasca.common.model.alarm.AlarmState;
import monasca.common.model.alarm.AlarmSubExpression;
import monasca.api.domain.exception.EntityNotFoundException;
import monasca.api.domain.exception.InvalidEntityException;
import monasca.api.domain.model.alarm.Alarm;
import monasca.api.domain.model.alarm.AlarmRepo;
import monasca.api.domain.model.alarmdefinition.AlarmDefinition;
import monasca.api.domain.model.alarmdefinition.AlarmDefinitionRepo;
import monasca.common.util.Exceptions;
import monasca.common.util.Serialization;
/**
* Services alarmed metric related requests.
*/
public class AlarmService {
private static final Logger LOG = LoggerFactory.getLogger(AlarmService.class);
private final ApiConfig config;
private final Producer<String, String> producer;
private final AlarmRepo repo;
private final AlarmDefinitionRepo alarmDefRepo;
private long messageCount = 0;
@Inject
public AlarmService(ApiConfig config, Producer<String, String> producer,
AlarmRepo repo, AlarmDefinitionRepo alarmDefRepo) {
this.config = config;
this.producer = producer;
this.repo = repo;
this.alarmDefRepo = alarmDefRepo;
}
/**
* Deletes the alarm identified by the {@code alarmId
* }.
*
* @throws EntityNotFoundException if the alarm cannot be found
*/
public void delete(String tenantId, String alarmId) {
Alarm alarm = repo.findById(tenantId, alarmId);
Map<String, AlarmSubExpression> subAlarmMetricDefs = repo.findAlarmSubExpressions(alarmId);
repo.deleteById(tenantId, alarmId);
// Notify interested parties of alarm deletion
String event =
Serialization.toJson(new AlarmDeletedEvent(tenantId, alarmId, alarm.getMetrics(), alarm
.getAlarmDefinition().getId(), subAlarmMetricDefs));
producer.send(new KeyedMessage<>(config.eventsTopic, String.valueOf(messageCount++), event));
}
/**
* Patches the alarm for the {@code tenantId} and {@code alarmId} 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 Alarm patch(String tenantId, String alarmId, AlarmState state) {
if (state == null) {
throw new InvalidEntityException("State must be specified");
}
Alarm alarm = updateInternal(tenantId, alarmId, state);
return alarm;
}
/**
* 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
*/
public Alarm update(String tenantId, String alarmId, UpdateAlarmCommand command) {
Alarm alarm = updateInternal(tenantId, alarmId, command.state);
return alarm;
}
private String stateChangeReasonFor(AlarmState oldState, AlarmState newState) {
return "Alarm state updated via API";
}
private Alarm updateInternal(String tenantId, String alarmId, AlarmState newState) {
try {
LOG.debug("Updating alarm {} for tenant {}", alarmId, tenantId);
final Alarm alarm = repo.update(tenantId, alarmId, newState);
final AlarmState oldState = alarm.getState();
// Notify interested parties of updated alarm
AlarmDefinition alarmDef = alarmDefRepo.findById(tenantId, alarm.getAlarmDefinition().getId());
Map<String, AlarmSubExpression> subAlarms = repo.findAlarmSubExpressions(alarmId);
String event =
Serialization.toJson(new AlarmUpdatedEvent(alarmId, alarmDef.getId(),
tenantId, alarm.getMetrics(), subAlarms, newState, oldState));
producer.send(new KeyedMessage<>(config.eventsTopic, String.valueOf(messageCount++), event));
// Notify interested parties of transitioned alarm state
if (!oldState.equals(newState)) {
event =
Serialization.toJson(new AlarmStateTransitionedEvent(tenantId, alarmId, alarmDef
.getId(), alarm.getMetrics(), alarmDef.getName(), alarmDef.getDescription(),
oldState, newState, alarmDef.getSeverity(), alarmDef.isActionsEnabled(),
stateChangeReasonFor(oldState, newState), null, System.currentTimeMillis()));
producer.send(new KeyedMessage<>(config.alarmStateTransitionsTopic, String.valueOf(messageCount++), event));
}
alarm.setState(newState);
return alarm;
} catch (EntityNotFoundException e) {
throw e;
} catch (Exception e) {
throw Exceptions.uncheck(e, "Error updating alarm for project / tenant %s", tenantId);
}
}
}

View File

@ -1,30 +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 monasca.api.app;
import javax.inject.Singleton;
import com.google.inject.AbstractModule;
/**
* Application layer bindings.
*/
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);
}
}

View File

@ -1,108 +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 monasca.api.app;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Nullable;
import javax.inject.Inject;
import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import monasca.api.ApiConfig;
import monasca.common.model.metric.Metric;
import monasca.common.model.metric.MetricEnvelope;
import monasca.common.model.metric.MetricEnvelopes;
/**
* Metric service implementation.
*/
public class MetricService {
private final ApiConfig config;
private final Producer<String, String> producer;
private final Meter metricMeter;
private final Meter batchMeter;
@Inject
public MetricService(ApiConfig config, Producer<String, String> producer,
MetricRegistry metricRegistry) {
this.config = config;
this.producer = producer;
metricMeter =
metricRegistry.meter(MetricRegistry.name(MetricService.class, "metrics.published"));
batchMeter =
metricRegistry.meter(MetricRegistry.name(MetricService.class, "batches.published"));
}
public void create(List<Metric> metrics, String tenantId, @Nullable String crossTenantId) {
String metricTenantId = Strings.isNullOrEmpty(crossTenantId) ? tenantId : crossTenantId;
Builder<String, Object> metaBuilder =
new ImmutableMap.Builder<String, Object>().put("tenantId", metricTenantId).put("region",
config.region);
ImmutableMap<String, Object> meta = metaBuilder.build();
List<KeyedMessage<String, String>> keyedMessages = new ArrayList<>(metrics.size());
for (Metric metric : metrics) {
MetricEnvelope envelope = new MetricEnvelope(metric, meta);
keyedMessages.add(new KeyedMessage<>(config.metricsTopic, buildKey(metricTenantId, metric),
MetricEnvelopes.toJson(envelope)));
metricMeter.mark();
}
producer.send(keyedMessages);
batchMeter.mark();
}
private String buildKey(String metricTenantId, Metric metric) {
final StringBuilder key = new StringBuilder(metricTenantId);
key.append(metric.name);
// Dimensions are optional.
if (metric.dimensions != null && !metric.dimensions.isEmpty()) {
// Key must be the same for the same metric so sort the dimensions so they will be
// in a known order
for (final Map.Entry<String, String> dim : buildSortedDimSet(metric.dimensions)) {
key.append(dim.getKey());
key.append(dim.getValue());
}
}
String keyValue = key.toString();
return keyValue;
}
private List<Map.Entry<String, String>> buildSortedDimSet(final Map<String, String> dimMap) {
final List<Map.Entry<String, String>> dims = new ArrayList<>(dimMap.entrySet());
Collections.sort(dims, new Comparator<Map.Entry<String, String>>() {
@Override
public int compare(Entry<String, String> o1, Entry<String, String> o2) {
int nameCmp = o1.getKey().compareTo(o2.getKey());
return (nameCmp != 0 ? nameCmp : o1.getValue().compareTo(o2.getValue()));
}
});
return dims;
}
}

View File

@ -1,124 +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 monasca.api.app.command;
import java.util.List;
import javax.annotation.Nullable;
import org.hibernate.validator.constraints.NotEmpty;
import monasca.api.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);
}
}

View File

@ -1,143 +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 monasca.api.app.command;
import java.util.Map;
import javax.annotation.Nullable;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
import com.fasterxml.jackson.annotation.JsonProperty;
import monasca.api.app.validation.DimensionValidation;
import monasca.api.app.validation.MetricNameValidation;
import monasca.api.app.validation.ValueMetaValidation;
import monasca.common.model.Services;
import monasca.common.model.metric.Metric;
import monasca.api.resource.exception.Exceptions;
public class CreateMetricCommand {
private static final long TIME_2MIN_MILLIS = 120*1000;
private static final long TIME_2WEEKS_MILLIS = 1209600*1000;
public static final int MAX_NAME_LENGTH = 255;
@NotEmpty
@Size(min = 1, max = MAX_NAME_LENGTH)
public String name;
public Map<String, String> dimensions;
public long timestamp;
public double value;
public Map<String, String> valueMeta;
public CreateMetricCommand() {}
public CreateMetricCommand(String name, @Nullable Map<String, String> dimensions,
@Nullable Long timestamp, double value, @Nullable Map<String, String> valueMeta) {
setName(name);
setDimensions(dimensions);
setTimestamp(timestamp);
setValueMeta(valueMeta);
this.value = value;
}
private static void validateTimestamp(long timestamp) {
long time = System.currentTimeMillis();
if (timestamp > time + TIME_2MIN_MILLIS || timestamp < time - TIME_2WEEKS_MILLIS)
throw Exceptions.unprocessableEntity("Timestamp %s is out of legal range", timestamp);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CreateMetricCommand other = (CreateMetricCommand) obj;
if (dimensions == null) {
if (other.dimensions != null)
return false;
} else if (!dimensions.equals(other.dimensions))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (timestamp != other.timestamp)
return false;
if (Double.doubleToLongBits(value) != Double.doubleToLongBits(other.value))
return false;
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dimensions == null) ? 0 : dimensions.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + (int) (timestamp ^ (timestamp >>> 32));
long temp;
temp = Double.doubleToLongBits(value);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@JsonProperty
public void setDimensions(Map<String, String> dimensions) {
this.dimensions =
dimensions == null || dimensions.isEmpty() ? null : DimensionValidation
.normalize(dimensions);
}
@JsonProperty
public void setValueMeta(Map<String, String> valueMeta) {
this.valueMeta = ValueMetaValidation.normalize(valueMeta);
}
@JsonProperty
public void setName(String name) {
this.name = MetricNameValidation.normalize(name);
}
@JsonProperty
public void setTimestamp(Long timestamp) {
this.timestamp =
timestamp == null || timestamp.longValue() == 0L ? System.currentTimeMillis()
: timestamp.longValue();
}
public Metric toMetric() {
return new Metric(name, dimensions, timestamp, value, valueMeta);
}
public void validate() {
// Validate name and dimensions
if (dimensions != null) {
String service = dimensions.get(Services.SERVICE_DIMENSION);
MetricNameValidation.validate(name, service, true);
DimensionValidation.validate(dimensions, service);
}
if (valueMeta != null) {
ValueMetaValidation.validate(valueMeta);
}
// Validate times and values
validateTimestamp(timestamp);
}
}

View File

@ -1,84 +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 monasca.api.app.command;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.commons.validator.routines.EmailValidator;
import org.apache.commons.validator.routines.UrlValidator;
import org.hibernate.validator.constraints.NotEmpty;
import monasca.api.domain.model.notificationmethod.NotificationMethodType;
import monasca.api.resource.exception.Exceptions;
public class CreateNotificationMethodCommand {
@NotEmpty
@Size(min = 1, max = 250)
public String name;
@NotNull
public NotificationMethodType type;
@NotEmpty
@Size(min = 1, max = 100)
public String address;
public CreateNotificationMethodCommand() {}
public CreateNotificationMethodCommand(String name, NotificationMethodType type, String address) {
this.name = name;
this.type = type;
this.address = address;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CreateNotificationMethodCommand other = (CreateNotificationMethodCommand) obj;
if (address == null) {
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (type != other.type)
return false;
return true;
}
public void validate() {
switch (type) {
case EMAIL : {
if (!EmailValidator.getInstance(true).isValid(address))
throw Exceptions.unprocessableEntity("Address %s is not of correct format", address);
}; break;
case WEBHOOK : {
String[] schemes = {"http","https"};
UrlValidator urlValidator = new UrlValidator(schemes, UrlValidator.ALLOW_LOCAL_URLS | UrlValidator.ALLOW_2_SLASHES);
if (!urlValidator.isValid(address))
throw Exceptions.unprocessableEntity("Address %s is not of correct format", address);
}; break;
case PAGERDUTY : {
// No known validation for PAGERDUTY type at this time
}; break;
}
}
}

View File

@ -1,29 +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 monasca.api.app.command;
import javax.validation.constraints.NotNull;
import monasca.common.model.alarm.AlarmState;
public class UpdateAlarmCommand {
@NotNull
public AlarmState state;
public UpdateAlarmCommand() {}
public UpdateAlarmCommand(AlarmState state) {
this.state = state;
}
}

View File

@ -1,59 +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 monasca.api.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;
}
}

View File

@ -1,20 +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.
*/
/**
* Houses the application/service layer.
*
* @see http://martinfowler.com/eaaCatalog/serviceLayer.html
*/
package monasca.api.app;

View File

@ -1,117 +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 monasca.api.app.validation;
import java.util.Arrays;
import java.util.List;
import javax.ws.rs.WebApplicationException;
import monasca.common.model.Services;
import monasca.common.model.alarm.AlarmExpression;
import monasca.common.model.alarm.AlarmSubExpression;
import monasca.common.model.metric.MetricDefinition;
import monasca.api.resource.exception.Exceptions;
/**
* Utilities for validating AlarmExpressions.
*/
public final class AlarmValidation {
private static final List<String> VALID_ALARM_SERVERITY = Arrays.asList("low", "medium", "high",
"critical");
private AlarmValidation() {}
/**
* @throws WebApplicationException if validation fails
*/
public static void validate(String name, String description, String severity,
List<String> alarmActions, List<String> okActions, List<String> undeterminedActions) {
if (name != null && name.length() > 255)
throw Exceptions.unprocessableEntity("Name %s must be 255 characters or less", name);
if (description != null && description.length() > 255)
throw Exceptions.unprocessableEntity("Description %s must be 255 characters or less",
description);
if (alarmActions != null)
for (String action : alarmActions)
if (action.length() > 50)
throw Exceptions.unprocessableEntity("Alarm action %s must be 50 characters or less",
action);
if (okActions != null)
for (String action : okActions)
if (action.length() > 50)
throw Exceptions
.unprocessableEntity("Ok action %s must be 50 characters or less", action);
if (undeterminedActions != null)
for (String action : undeterminedActions)
if (action.length() > 50)
throw Exceptions.unprocessableEntity(
"Undetermined action %s must be 50 characters or less", action);
if (severity != null && !VALID_ALARM_SERVERITY.contains(severity.toLowerCase())) {
throw Exceptions.unprocessableEntity("%s is not a valid severity", severity);
}
}
/**
* Validates, normalizes and gets an AlarmExpression for the {@code expression}.
*
* @throws WebApplicationException if validation fails
*/
public static AlarmExpression validateNormalizeAndGet(String expression) {
AlarmExpression alarmExpression = null;
try {
alarmExpression = AlarmExpression.of(expression);
} catch (IllegalArgumentException e) {
throw Exceptions.unprocessableEntityDetails("The alarm expression is invalid",
e.getMessage(), e);
}
for (AlarmSubExpression subExpression : alarmExpression.getSubExpressions()) {
MetricDefinition metricDef = subExpression.getMetricDefinition();
String service =
metricDef.dimensions == null ? null : metricDef.dimensions
.get(Services.SERVICE_DIMENSION);
// Normalize and validate namespace
metricDef.name = MetricNameValidation.normalize(metricDef.name);
MetricNameValidation.validate(metricDef.name, service, true);
// Normalize and validate dimensions
if (metricDef.dimensions != null) {
metricDef.setDimensions(DimensionValidation.normalize(metricDef.dimensions));
DimensionValidation.validate(metricDef.dimensions, service);
}
// Validate period
if (subExpression.getPeriod() == 0)
throw Exceptions.unprocessableEntity("Period must not be 0");
if (subExpression.getPeriod() % 60 != 0)
throw Exceptions.unprocessableEntity("Period %s must be a multiple of 60",
subExpression.getPeriod());
// Validate periods
if (subExpression.getPeriods() < 1)
throw Exceptions.unprocessableEntity("Periods %s must be greater than or equal to 1",
subExpression.getPeriods());
if (subExpression.getPeriod() * subExpression.getPeriods() > 1209600)
throw Exceptions.unprocessableEntity(
"Period %s times %s must total less than 2 weeks in seconds (1209600)",
subExpression.getPeriod(), subExpression.getPeriods());
}
return alarmExpression;
}
}

View File

@ -1,181 +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 monasca.api.app.validation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import javax.ws.rs.WebApplicationException;
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import com.google.common.primitives.Ints;
import monasca.common.model.Services;
import monasca.api.resource.exception.Exceptions;
/**
* Utilities for validating dimensions.
*/
public final class DimensionValidation {
private static final Map<String, DimensionValidator> VALIDATORS;
private static final Pattern UUID_PATTERN = Pattern
.compile("\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}");
private static final Pattern VALID_DIMENSION_NAME = Pattern.compile("[^><={}(), '\";&]+$");
private static final String INVALID_CHAR_STRING = "> < = { } ( ) ' \" , ; &";
private DimensionValidation() {}
interface DimensionValidator {
boolean isValidDimension(String name, String value);
}
static {
VALIDATORS = new HashMap<String, DimensionValidator>();
// Compute validator
VALIDATORS.put(Services.COMPUTE_SERVICE, new DimensionValidator() {
@Override
public boolean isValidDimension(String name, String value) {
if ("instance_id".equals(name))
return value.length() != 36 || UUID_PATTERN.matcher(value).matches();
if ("az".equals(name))
return Ints.tryParse(value) != null;
return true;
}
});
// Objectstore validator
VALIDATORS.put(Services.OBJECT_STORE_SERVICE, new DimensionValidator() {
@Override
public boolean isValidDimension(String name, String value) {
if ("container".equals(name))
return value.length() < 256 || !value.contains("/");
return true;
}
});
// Volume validator
VALIDATORS.put(Services.VOLUME_SERVICE, new DimensionValidator() {
@Override
public boolean isValidDimension(String name, String value) {
if ("instance_id".equals(name))
return value.length() != 36 || UUID_PATTERN.matcher(value).matches();
if ("az".equals(name))
return Ints.tryParse(value) != null;
return true;
}
});
}
/**
* Normalizes dimensions by stripping whitespace.
*/
public static Map<String, String> normalize(Map<String, String> dimensions) {
if (dimensions == null)
return null;
Map<String, String> result = new HashMap<>();
for (Map.Entry<String, String> dimension : dimensions.entrySet()) {
String dimensionKey = null;
if (dimension.getKey() != null) {
dimensionKey = CharMatcher.WHITESPACE.trimFrom(dimension.getKey());
if (dimensionKey.isEmpty())
dimensionKey = null;
}
String dimensionValue = null;
if (dimension.getValue() != null) {
dimensionValue = CharMatcher.WHITESPACE.trimFrom(dimension.getValue());
if (dimensionValue.isEmpty())
dimensionValue = null;
}
result.put(dimensionKey, dimensionValue);
}
return result;
}
/**
* Validates that the given {@code dimensions} are valid.
*
* @throws WebApplicationException if validation fails
*/
public static void validate(Map<String, String> dimensions, @Nullable String service) {
// Validate dimension names and values
for (Map.Entry<String, String> dimension : dimensions.entrySet()) {
String name = dimension.getKey();
String value = dimension.getValue();
// General validations
if (Strings.isNullOrEmpty(name))
throw Exceptions.unprocessableEntity("Dimension name cannot be empty");
if (Strings.isNullOrEmpty(value))
throw Exceptions.unprocessableEntity("Dimension %s cannot have an empty value", name);
if (name.length() > 255)
throw Exceptions.unprocessableEntity("Dimension name %s must be 255 characters or less",
name);
if (value.length() > 255)
throw Exceptions.unprocessableEntity("Dimension value %s must be 255 characters or less",
value);
// Dimension names that start with underscores are reserved for internal use only.
if (name.startsWith("_")) {
throw Exceptions.unprocessableEntity("Dimension name cannot start with underscore (_)",
name);
}
if (!VALID_DIMENSION_NAME.matcher(name).matches())
throw Exceptions.unprocessableEntity(
"Dimension name %s may not contain: %s", name, INVALID_CHAR_STRING);
// Service specific validations
if (service != null) {
if (!name.equals(Services.SERVICE_DIMENSION)
&& !Services.isValidDimensionName(service, name))
throw Exceptions.unprocessableEntity("%s is not a valid dimension name for service %s",
name, service);
DimensionValidator validator = VALIDATORS.get(service);
if (validator != null && !validator.isValidDimension(name, value))
throw Exceptions.unprocessableEntity("%s is not a valid dimension value for service %s",
value, service);
}
}
}
/**
* Validates a list of dimension names
* @param names
*/
public static void validateNames(List<String> names) {
if(names != null) {
for (String name : names) {
if (Strings.isNullOrEmpty(name)) {
throw Exceptions.unprocessableEntity("Dimension name cannot be empty");
}
if (name.length() > 255) {
throw Exceptions.unprocessableEntity("Dimension name '%s' must be 255 characters or less",
name);
}
// Dimension names that start with underscores are reserved for internal use only.
if (name.startsWith("_")) {
throw Exceptions.unprocessableEntity("Dimension name '%s' cannot start with underscore (_)",
name);
}
if (!VALID_DIMENSION_NAME.matcher(name).matches())
throw Exceptions.unprocessableEntity(
"Dimension name '%s' may not contain: %s", name, INVALID_CHAR_STRING);
}
}
}
}

View File

@ -1,74 +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 monasca.api.app.validation;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import monasca.common.model.Services;
import monasca.api.app.command.CreateMetricCommand;
import monasca.api.resource.exception.Exceptions;
import com.sun.jersey.spi.container.WebApplication;
/**
* Utilities for validating metric names.
*/
public class MetricNameValidation {
private static final Pattern VALID_METRIC_NAME = Pattern.compile("[^><={}(), '\";&]+$");
private MetricNameValidation() {}
/**
* Normalizes the {@code metricName} by removing whitespace.
*/
public static String normalize(String metricName) {
return metricName == null ? null : CharMatcher.WHITESPACE.trimFrom(metricName);
}
/**
* Validates the {@code metricName} for the character constraints.
*
* @throws WebApplication if validation fails
*/
public static void validate(String metricName, @Nullable String service, boolean nameRequiredFlag) {
// General validations
if (Strings.isNullOrEmpty(metricName)) {
if (nameRequiredFlag) {
throw Exceptions.unprocessableEntity("Metric name is required");
} else {
return;
}
}
if (metricName.length() > CreateMetricCommand.MAX_NAME_LENGTH)
throw Exceptions.unprocessableEntity("Metric name %s must be %d characters or less",
metricName, CreateMetricCommand.MAX_NAME_LENGTH);
if (!Services.isReserved(metricName) && !VALID_METRIC_NAME.matcher(metricName).matches())
throw Exceptions.unprocessableEntity("Metric name %s may not contain: > < = { } ( ) ' \" , ; &",
metricName);
// Service specific validations
if (service != null && Services.isReserved(service)) {
if (!Strings.isNullOrEmpty(metricName) && !Services.isValidMetricName(service, metricName)) {
throw Exceptions.unprocessableEntity("%s is not a valid metric name for namespace %s",
metricName, service);
}
}
}
}

View File

@ -1,167 +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 monasca.api.app.validation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.WebApplicationException;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import monasca.common.model.Services;
import monasca.api.resource.exception.Exceptions;
/**
* Validation related utilities.
*/
public final class Validation {
private static final Splitter COMMA_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults();
private static final Splitter COLON_SPLITTER = Splitter.on(':').omitEmptyStrings().trimResults().limit(2);
private static final DateTimeFormatter ISO_8601_FORMATTER = ISODateTimeFormat
.dateOptionalTimeParser().withZoneUTC();
private static final List<String> VALID_STATISTICS = Arrays.asList("avg", "min", "max", "sum",
"count");
private static final List<String> VALID_ALARM_STATE = Arrays
.asList("undetermined", "ok", "alarm");
private Validation() {}
/**
* @throws JsonMappingException if the {@code value} is not valid for the {@code type}
*/
public static <T extends Enum<T>> T parseAndValidate(Class<T> type, String value)
throws JsonMappingException {
for (T constant : type.getEnumConstants())
if (constant.name().equalsIgnoreCase(value))
return constant;
List<String> acceptedValues = new ArrayList<>();
for (T constant : type.getEnumConstants())
acceptedValues.add(constant.name());
throw new JsonMappingException(String.format("%s was not one of %s", value, acceptedValues));
}
/**
* @throws WebApplicationException if the {@code date} invalid or is required and null.
*/
public static DateTime parseAndValidateDate(String date, String parameterName, boolean required) {
if (Strings.isNullOrEmpty(date)) {
if (required)
throw Exceptions.unprocessableEntity("%s is required", parameterName);
else
return null;
}
try {
return ISO_8601_FORMATTER.parseDateTime(date);
} catch (Exception e) {
throw Exceptions.unprocessableEntity("%s must be an ISO 8601 formatted time", parameterName);
}
}
/**
* @throws WebApplicationException if the {@code value} is null or empty.
*/
public static Map<String, String> parseAndValidateNameAndDimensions(String name,
String dimensionsStr,
boolean nameRequiredFlag) {
Map<String, String> dimensions = parseAndValidateDimensions(dimensionsStr);
String service = dimensions.get(Services.SERVICE_DIMENSION);
MetricNameValidation.validate(name, service, nameRequiredFlag);
return dimensions;
}
/**
* @throws WebApplicationException if the {@code value} is null or empty.
*/
public static Map<String, String> parseAndValidateDimensions(String dimensionsStr) {
Validation.validateNotNullOrEmpty(dimensionsStr, "dimensions");
Map<String, String> dimensions = new HashMap<String, String>();
for (String dimensionStr : COMMA_SPLITTER.split(dimensionsStr)) {
String[] dimensionArr = Iterables.toArray(COLON_SPLITTER.split(dimensionStr), String.class);
if (dimensionArr.length == 2)
dimensions.put(dimensionArr[0], dimensionArr[1]);
}
String service = dimensions.get(Services.SERVICE_DIMENSION);
DimensionValidation.validate(dimensions, service);
return dimensions;
}
/**
* @throws WebApplicationException if the {@code number} is invalid.
*/
public static int parseAndValidateNumber(String number, String parameterName) {
try {
return Integer.parseInt(number);
} catch (NumberFormatException e) {
throw Exceptions.unprocessableEntity("%s must be valid number", parameterName);
}
}
/**
* @throws WebApplicationException if the {@code statistics} empty or invalid.
*/
public static List<String> parseValidateAndNormalizeStatistics(Iterable<String> statistics) {
List<String> validStats = new ArrayList<String>(5);
for (String statistic : statistics) {
String statisticLower = statistic.toLowerCase();
if (!VALID_STATISTICS.contains(statisticLower))
throw Exceptions.unprocessableEntity("%s is not a valid statistic", statistic);
validStats.add(statisticLower);
}
if (validStats.isEmpty())
throw Exceptions.unprocessableEntity("Statistics are required");
return validStats;
}
/**
* @throws WebApplicationException if the {@code statistics} empty or invalid.
*/
public static void validateAlarmState(String state) {
String stateLower = state.toLowerCase();
if (!VALID_ALARM_STATE.contains(stateLower)) {
throw Exceptions.unprocessableEntity("%s is not a valid state", state);
}
}
/**
* @throws WebApplicationException if the {@code value} is null or empty.
*/
public static void validateNotNullOrEmpty(String value, String parameterName) {
if (Strings.isNullOrEmpty(value))
throw Exceptions.unprocessableEntity("%s is required", parameterName);
}
/**
* @throws WebApplicationException if the {@code startTime} or {@code endTime} are invalid
*/
public static void validateTimes(DateTime startTime, DateTime endTime) {
if (!startTime.isBefore(endTime))
throw Exceptions.badRequest("start_time must be before end_time");
}
}

View File

@ -1,95 +0,0 @@
/*
* Copyright (c) 2015 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 monasca.api.app.validation;
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import monasca.api.resource.exception.Exceptions;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.WebApplicationException;
/**
* Utilities for validating valueMeta.
*/
public final class ValueMetaValidation {
private static final int VALUE_META_MAX_NUMBER = 16;
private static final int VALUE_META_VALUE_MAX_LENGTH = 2048;
private static final int VALUE_META_NAME_MAX_LENGTH = 255;
private static final Map<String, String> EMPTY_VALUE_META = Collections
.unmodifiableMap(new HashMap<String, String>());
private ValueMetaValidation() {}
/**
* Normalizes valueMeta by stripping whitespace from name. validate() must
* already have been called on the valueMeta
*/
public static Map<String, String> normalize(Map<String, String> valueMeta) {
if (valueMeta == null || valueMeta.isEmpty()) {
return EMPTY_VALUE_META;
}
final Map<String, String> result = new HashMap<>();
for (Map.Entry<String, String> entry : valueMeta.entrySet()) {
final String key = CharMatcher.WHITESPACE.trimFrom(entry.getKey());
result.put(key, entry.getValue());
}
return result;
}
/**
* Validates that the given {@code valueMetas} are valid.
*
* @throws WebApplicationException if validation fails
*/
public static void validate(Map<String, String> valueMetas) {
if (valueMetas.size() > VALUE_META_MAX_NUMBER) {
throw Exceptions.unprocessableEntity("Maximum number of valueMeta key/value parirs is %d",
VALUE_META_MAX_NUMBER);
}
// Validate valueMeta names and values
for (Map.Entry<String, String> valueMeta : valueMetas.entrySet()) {
// Have to check for null first because later check is for trimmed name
if (valueMeta.getKey() == null) {
throw Exceptions.unprocessableEntity("valueMeta name cannot be empty");
}
final String name = CharMatcher.WHITESPACE.trimFrom(valueMeta.getKey());
String value = valueMeta.getValue();
if (value == null) {
// Store nulls as empty strings
value = "";
}
// General validations
if (Strings.isNullOrEmpty(name)) {
throw Exceptions.unprocessableEntity("valueMeta name cannot be empty");
}
if (name.length() > VALUE_META_NAME_MAX_LENGTH) {
throw Exceptions.unprocessableEntity("valueMeta name %s must be %d characters or less",
name, VALUE_META_NAME_MAX_LENGTH);
}
if (value.length() > VALUE_META_VALUE_MAX_LENGTH) {
throw Exceptions.unprocessableEntity("valueMeta value %s must be %d characters or less",
value, VALUE_META_VALUE_MAX_LENGTH);
}
}
}
}

View File

@ -1,30 +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 monasca.api.domain;
import javax.inject.Singleton;
import com.google.inject.AbstractModule;
import monasca.api.domain.model.version.VersionRepo;
import monasca.api.domain.service.impl.VersionRepoImpl;
/**
* Domain layer bindings.
*/
public class DomainModule extends AbstractModule {
@Override
protected void configure() {
bind(VersionRepo.class).to(VersionRepoImpl.class).in(Singleton.class);
}
}

View File

@ -1,37 +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 monasca.api.domain.exception;
/**
* Indicates that a domain entity already exists.
*/
public class EntityExistsException extends RuntimeException {
private static final long serialVersionUID = 1L;
public EntityExistsException(Exception ex, String msg) {
super(msg, ex);
}
public EntityExistsException(Exception ex, String msg, Object... args) {
super(String.format(msg, args), ex);
}
public EntityExistsException(String msg) {
super(msg);
}
public EntityExistsException(String msg, Object... args) {
super(String.format(msg, args));
}
}

View File

@ -1,37 +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 monasca.api.domain.exception;
/**
* Indicates that a domain entity is unknown.
*/
public class EntityNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
public EntityNotFoundException(Exception ex, String msg) {
super(msg, ex);
}
public EntityNotFoundException(Exception ex, String msg, Object... args) {
super(String.format(msg, args), ex);
}
public EntityNotFoundException(String msg) {
super(msg);
}
public EntityNotFoundException(String msg, Object... args) {
super(String.format(msg, args));
}
}

View File

@ -1,37 +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 monasca.api.domain.exception;
/**
* Indicates that an entity is invalid.
*/
public class InvalidEntityException extends RuntimeException {
private static final long serialVersionUID = 1L;
public InvalidEntityException(Exception ex, String msg) {
super(msg, ex);
}
public InvalidEntityException(Exception ex, String msg, Object... args) {
super(String.format(msg, args), ex);
}
public InvalidEntityException(String msg) {
super(msg);
}
public InvalidEntityException(String msg, Object... args) {
super(String.format(msg, args));
}
}

View File

@ -1,72 +0,0 @@
/*
* Copyright (c) 2015 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 monasca.api.domain.exception;
import java.util.HashMap;
import java.util.Map;
public class MultipleMetricsException extends Exception {
private String metricName;
private Map<String, String> dimensions;
public MultipleMetricsException() {
super();
init(null, null);
}
public MultipleMetricsException(String metricName, Map<String, String> dimensions) {
super();
init(metricName, dimensions);
}
public MultipleMetricsException(String metricName, Map<String, String> dimensions,
String message) {
super(message);
init(metricName, dimensions);
}
public MultipleMetricsException(String metricName, Map<String, String> dimensions,
String message, Throwable cause) {
super(message, cause);
init(metricName, dimensions);
}
public MultipleMetricsException(String metricName, Map<String, String> dimensions,
Throwable cause) {
super(cause);
init(metricName, dimensions);
}
public MultipleMetricsException(String metricName, Map<String, String> dimensions,
String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
init(metricName, dimensions);
}
private void init(String metricName, Map<String, String> dimensions) {
this.metricName = metricName == null ? "" : metricName;
this.dimensions = dimensions == null ? new HashMap<String, String>() : dimensions;
}
public String getMetricName() {
return metricName;
}
public Map<String, String> getDimensions() {
return dimensions;
}
}

View File

@ -1,236 +0,0 @@
/*
* Copyright (c) 2015 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 monasca.api.domain.model.alarm;
import org.joda.time.DateTime;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import monasca.common.model.alarm.AlarmState;
import monasca.common.model.metric.MetricDefinition;
import monasca.common.model.domain.common.AbstractEntity;
import monasca.api.domain.model.common.Link;
import monasca.api.domain.model.common.Linked;
@XmlRootElement(name = "Alarm")
public class Alarm extends AbstractEntity implements Linked {
private List<Link> links;
private List<MetricDefinition> metrics;
private AlarmState state;
private AlarmDefinitionShort alarmDefinition;
private DateTime stateUpdatedTimestamp;
private DateTime createdTimestamp;
public Alarm() {}
public Alarm(String id, String alarmDefinitionId, String alarmDefinitionName,
String alarmDefinitionSeverity, List<MetricDefinition> metrics, AlarmState state, DateTime stateUpdatedTimestamp, DateTime createdAt) {
this.id = id;
setMetrics(metrics);
setState(state);
setStateUpdatedTimestamp(stateUpdatedTimestamp);
setCreatedTimestamp(createdAt);
this.alarmDefinition = new AlarmDefinitionShort(alarmDefinitionId, alarmDefinitionName, alarmDefinitionSeverity);
}
public String getId() {
return id;
}
public List<Link> getLinks() {
return links;
}
public AlarmState getState() {
return state;
}
public DateTime getStateUpdatedTimestamp() { return stateUpdatedTimestamp; }
public DateTime getCreatedTimestamp() { return createdTimestamp; }
@XmlElement(name = "id")
public void setId(String id) {
this.id = id;
}
@Override
public void setLinks(List<Link> links) {
this.links = links;
}
public void setState(AlarmState state) {
this.state = state;
}
public void setStateUpdatedTimestamp(DateTime stateUpdatedTimestamp) {
this.stateUpdatedTimestamp = stateUpdatedTimestamp;
}
public void setCreatedTimestamp(DateTime createdTimestamp) {
this.createdTimestamp = createdTimestamp;
}
public List<MetricDefinition> getMetrics() {
return metrics;
}
public void setMetrics(List<MetricDefinition> metrics) {
this.metrics = metrics;
}
public AlarmDefinitionShort getAlarmDefinition() {
return alarmDefinition;
}
public void setAlarmDefinition(AlarmDefinitionShort alarmDefinition) {
this.alarmDefinition = alarmDefinition;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((alarmDefinition == null) ? 0 : alarmDefinition.hashCode());
result = prime * result + ((links == null) ? 0 : links.hashCode());
result = prime * result + ((metrics == null) ? 0 : metrics.hashCode());
result = prime * result + ((state == null) ? 0 : state.hashCode());
result = prime * result + ((stateUpdatedTimestamp == null) ? 0 : stateUpdatedTimestamp.hashCode());
result = prime * result + ((createdTimestamp == null) ? 0 : createdTimestamp.hashCode());
return result;
}
@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 (alarmDefinition == null) {
if (other.alarmDefinition != null)
return false;
} else if (!alarmDefinition.equals(other.alarmDefinition))
return false;
if (links == null) {
if (other.links != null)
return false;
} else if (!links.equals(other.links))
return false;
if (metrics == null) {
if (other.metrics != null)
return false;
} else if (!metrics.equals(other.metrics))
return false;
if (state != other.state)
return false;
// Ignore timezones, only check milliseconds since epoch
if (stateUpdatedTimestamp.getMillis() != other.stateUpdatedTimestamp.getMillis()) {
return false;
}
if (createdTimestamp.getMillis() != other.createdTimestamp.getMillis()) {
return false;
}
return true;
}
/**
* This class holds the parts of AlarmDefinition that are returned through the API with
* an Alarm
* @author craigbr
*
*/
public static class AlarmDefinitionShort extends AbstractEntity implements Linked {
private String name;
private String severity;
private List<Link> links;
public AlarmDefinitionShort() {
}
public AlarmDefinitionShort(String id, String name, String severity) {
this.id = id;
this.name = name;
this.severity = severity;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSeverity() {
return severity;
}
public void setSeverity(String severity) {
this.severity = severity;
}
@Override
public List<Link> getLinks() {
return links;
}
@Override
public void setLinks(List<Link> links) {
this.links = links;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((links == null) ? 0 : links.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((severity == null) ? 0 : severity.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
AlarmDefinitionShort other = (AlarmDefinitionShort) obj;
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 (severity == null) {
if (other.severity != null)
return false;
} else if (!severity.equals(other.severity))
return false;
return true;
}
}
}

View File

@ -1,58 +0,0 @@
/*
* Copyright (c) 2015 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 monasca.api.domain.model.alarm;
import org.joda.time.DateTime;
import java.util.List;
import java.util.Map;
import monasca.common.model.alarm.AlarmState;
import monasca.common.model.alarm.AlarmSubExpression;
import monasca.api.domain.exception.EntityNotFoundException;
public interface AlarmRepo {
/**
* Deletes all alarms associated with the {@code id}.
*/
void deleteById(String tenantId, String id);
/**
* Returns alarms for the given criteria.
*/
List<Alarm> find(String tenantId, String alarmDefId, String metricName, Map<String,
String> metricDimensions, AlarmState state, DateTime stateUpdatedStart, String offset, int limit, boolean enforceLimit);
/**
* @throws EntityNotFoundException if an alarm cannot be found for the {@code id}
*/
Alarm findById(String tenantId, String id);
/**
* Updates the state and returns the original alarm for the {@code id}.
* @return the original alarm before any state change
*/
Alarm update(String tenantId, String id, AlarmState state);
/**
* Gets the AlarmSubExpressions mapped by their Ids for an Alarm Id
*/
Map<String, AlarmSubExpression> findAlarmSubExpressions(String alarmId);
/**
* Gets the AlarmSubExpressions mapped by their Ids then mapped by alarm id for an
* Alarm Definition Id
*/
Map<String, Map<String, AlarmSubExpression>> findAlarmSubExpressionsForAlarmDefinition(String alarmDefinitionId);
}

View File

@ -1,246 +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 monasca.api.domain.model.alarmdefinition;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.Collections;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import monasca.api.domain.model.common.Link;
import monasca.api.domain.model.common.Linked;
import monasca.common.model.alarm.AlarmExpression;
import monasca.common.model.domain.common.AbstractEntity;
@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")
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);
}
}

View File

@ -1,76 +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 monasca.api.domain.model.alarmdefinition;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import monasca.common.model.alarm.AlarmSubExpression;
import monasca.common.model.metric.MetricDefinition;
import monasca.api.domain.exception.EntityNotFoundException;
/**
* Repository for alarm definitions.
*/
public interface AlarmDefinitionRepo {
/**
* 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.
*/
String exists(String tenantId, String name);
/**
* Returns alarms for the given criteria.
*/
List<AlarmDefinition> find(String tenantId, String name, Map<String, String> dimensions,
String offset, int limit);
/**
* @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);
}

View File

@ -1,187 +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 monasca.api.domain.model.alarmstatehistory;
import java.util.List;
import monasca.common.model.alarm.AlarmTransitionSubAlarm;
import org.joda.time.DateTime;
import monasca.common.model.alarm.AlarmState;
import monasca.common.model.domain.common.AbstractEntity;
import monasca.common.model.metric.MetricDefinition;
public class AlarmStateHistory extends AbstractEntity {
private String alarmId;
private List<MetricDefinition> metrics;
private AlarmState oldState;
private AlarmState newState;
private String reason;
private String reasonData;
private DateTime timestamp;
private List<AlarmTransitionSubAlarm> subAlarms;
public AlarmStateHistory() {}
public AlarmStateHistory(String alarmId, List<MetricDefinition> metrics, AlarmState oldState,
AlarmState newState, List<AlarmTransitionSubAlarm> subAlarms, String reason, String reasonData, DateTime timestamp) {
id = new Long(timestamp.getMillis()).toString();
this.alarmId = alarmId;
this.setMetrics(metrics);
this.oldState = oldState;
this.newState = newState;
this.subAlarms = subAlarms;
this.reason = reason;
this.reasonData = reasonData;
this.timestamp = timestamp;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof AlarmStateHistory))
return false;
AlarmStateHistory other = (AlarmStateHistory) obj;
if (alarmId == null) {
if (other.alarmId != null)
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)
return false;
if (subAlarms == null) {
if (other.subAlarms != null)
return false;
} else if (!subAlarms.equals(other.subAlarms))
return false;
if (reason == null) {
if (other.reason != null)
return false;
} else if (!reason.equals(other.reason))
return false;
if (reasonData == null) {
if (other.reasonData != null)
return false;
} else if (!reasonData.equals(other.reasonData))
return false;
if (timestamp == null) {
if (other.timestamp != null)
return false;
} else if (!timestamp.equals(other.timestamp))
return false;
return true;
}
public void setId(String id) {
this.id = id;
}
public String getAlarmId() {
return alarmId;
}
public List<MetricDefinition> getMetrics() {
return metrics;
}
public AlarmState getNewState() {
return newState;
}
public AlarmState getOldState() {
return oldState;
}
public String getReason() {
return reason;
}
public String getReasonData() {
return reasonData;
}
public DateTime getTimestamp() {
return timestamp;
}
@Override
public int hashCode() {
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 + ((subAlarms == null) ? 0 : subAlarms.hashCode());
result = prime * result + ((reason == null) ? 0 : reason.hashCode());
result = prime * result + ((reasonData == null) ? 0 : reasonData.hashCode());
result = prime * result + ((timestamp == null) ? 0 : timestamp.hashCode());
return result;
}
public void setAlarmId(String alarmId) {
this.alarmId = alarmId;
}
public void setMetrics(List<MetricDefinition> metrics) {
this.metrics = metrics;
}
public void setNewState(AlarmState newState) {
this.newState = newState;
}
public void setOldState(AlarmState oldState) {
this.oldState = oldState;
}
public void setReason(String reason) {
this.reason = reason;
}
public void setReasonData(String reasonData) {
this.reasonData = reasonData;
}
public List<AlarmTransitionSubAlarm> getSubAlarms() {
return subAlarms;
}
public void setSubAlarms(List<AlarmTransitionSubAlarm> subAlarms) {
this.subAlarms = subAlarms;
}
public void setTimestamp(DateTime timestamp) {
this.timestamp = timestamp;
// Set the id in the AbstractEntity class.
id = new Long(timestamp.getMillis()).toString();
}
@Override
public String toString() {
return "AlarmStateHistory [alarmId=" + alarmId + ", metrics=" + metrics + ", oldState="
+ oldState + ", newState=" + newState + ", subAlarms=" + subAlarms + ", reason=" + reason + ", reasonData=" + reasonData
+ ", timestamp=" + timestamp + "]";
}
}

View File

@ -1,37 +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 monasca.api.domain.model.alarmstatehistory;
import monasca.api.domain.exception.EntityNotFoundException;
import org.joda.time.DateTime;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
/**
* Repository for alarm state history.
*/
public interface AlarmStateHistoryRepo {
/**
* @throws EntityNotFoundException if an alarm cannot be found for the {@code alarmId}
*/
List<AlarmStateHistory> findById(String tenantId, String alarmId, String offset, int limit) throws Exception;
/**
* Finds AlarmStateHistory for the given criteria.
*/
List<AlarmStateHistory> find(String tenantId, Map<String, String> dimensions,
DateTime startTime, @Nullable DateTime endTime, @Nullable String offset, int limit) throws Exception;
}

View File

@ -1,63 +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 monasca.api.domain.model.common;
public class Link {
public String rel;
public String href;
public Link() {}
public Link(String rel, String href) {
this.rel = rel;
this.href = href;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Link other = (Link) obj;
if (href == null) {
if (other.href != null)
return false;
} else if (!href.equals(other.href))
return false;
if (rel == null) {
if (other.rel != null)
return false;
} else if (!rel.equals(other.rel))
return false;
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((href == null) ? 0 : href.hashCode());
result = prime * result + ((rel == null) ? 0 : rel.hashCode());
return result;
}
@Override
public String toString() {
return String.format("Link [rel=%s, href=%s]", rel, href);
}
}

View File

@ -1,25 +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 monasca.api.domain.model.common;
import java.util.List;
/**
* Defines a type that can be described via a set of links.
*/
public interface Linked {
List<Link> getLinks();
void setLinks(List<Link> links);
}

View File

@ -1,27 +0,0 @@
/*
* Copyright (c) 2015 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 monasca.api.domain.model.common;
import java.util.ArrayList;
import java.util.List;
public class Paged {
public static final int LIMIT = 10000;
public List<Link> links = new ArrayList<>();
public List<?> elements;
}

View File

@ -1,32 +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 monasca.api.domain.model.measurement;
import org.joda.time.DateTime;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
/**
* Repository for measurements.
*/
public interface MeasurementRepo {
/**
* Finds measurements for the given criteria.
*/
List<Measurements> find(String tenantId, String name, Map<String, String> dimensions,
DateTime startTime, @Nullable DateTime endTime, @Nullable String offset,
int limit, Boolean mergeMetricsFlag) throws Exception;
}

View File

@ -1,125 +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 monasca.api.domain.model.measurement;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import monasca.common.model.domain.common.AbstractEntity;
/**
* Encapsulates a metric measurements.
*/
public class Measurements extends AbstractEntity {
private static final String[] COLUMNS = new String[] {"timestamp", "value", "value_meta"};
private String name;
private Map<String, String> dimensions;
private final String[] columns = COLUMNS;
private List<Object[]> measurements;
public Measurements() {}
public Measurements(String name, Map<String, String> dimensions, List<Object[]> measurements) {
this.name = name;
this.dimensions = dimensions;
this.measurements = measurements;
}
public Measurements(String name, Map<String, String> dimensions) {
this.name = name;
this.dimensions = dimensions;
this.measurements = new LinkedList<>();
}
public void addMeasurement(Object[] measurement) {
measurements.add(measurement);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Measurements other = (Measurements) obj;
if (dimensions == null) {
if (other.dimensions != null)
return false;
} else if (!dimensions.equals(other.dimensions))
return false;
if (measurements == null) {
if (other.measurements != null)
return false;
} else if (!measurements.equals(other.measurements))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public String[] getColumns() {
return columns;
}
public Map<String, String> getDimensions() {
return dimensions;
}
public List<Object[]> getMeasurements() {
return measurements;
}
public String getName() {
return name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dimensions == null) ? 0 : dimensions.hashCode());
result = prime * result + ((measurements == null) ? 0 : measurements.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
public void setDimensions(Map<String, String> dimensions) {
this.dimensions = dimensions;
}
public void setMeasurements(List<Object[]> measurements) {
this.measurements = measurements;
}
public void setName(String name) {
this.name = name;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return String.format("Measurement [name=%s, dimensions=%s, measurements=%s]", name, dimensions,
measurements);
}
}

View File

@ -1,34 +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 monasca.api.domain.model.metric;
import monasca.common.model.metric.MetricDefinition;
import java.util.List;
import java.util.Map;
/**
* Repository for metrics.
*/
public interface MetricDefinitionRepo {
/**
* Finds metrics for the given criteria.
*/
List<MetricDefinition> find(String tenantId, String name, Map<String, String> dimensions,
String offset, int limit)
throws Exception;
List<MetricName> findNames(String tenantId, Map<String, String> dimensions, String offset, int limit) throws Exception;
}

View File

@ -1,78 +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 monasca.api.domain.model.metric;
import monasca.common.model.domain.common.AbstractEntity;
public class MetricName extends AbstractEntity implements Comparable<MetricName> {
private String id;
private String name;
public MetricName(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
MetricName other = (MetricName) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
public String getId() {return id;}
public String getName() {return name;}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
public void setId(String id) {this.id = id;}
public void setName(String name) {this.name = name;}
@Override
public int compareTo(MetricName other) {
return this.name.compareTo(other.name);
}
}

View File

@ -1,110 +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 monasca.api.domain.model.notificationmethod;
import java.util.List;
import monasca.common.model.domain.common.AbstractEntity;
import monasca.api.domain.model.common.Link;
import monasca.api.domain.model.common.Linked;
public class NotificationMethod extends AbstractEntity implements Linked {
private List<Link> links;
private String name;
private NotificationMethodType type;
private String address;
public NotificationMethod() {}
public NotificationMethod(String id, String name, NotificationMethodType type, String address) {
this.id = id;
this.name = name;
this.type = type;
this.address = address;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
NotificationMethod other = (NotificationMethod) obj;
if (address == null) {
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (type != other.type)
return false;
return true;
}
public String getAddress() {
return address;
}
public String getId() {
return id;
}
public List<Link> getLinks() {
return links;
}
public String getName() {
return name;
}
public NotificationMethodType getType() {
return type;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((address == null) ? 0 : address.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
public void setAddress(String address) {
this.address = address;
}
public void setId(String id) {
this.id = id;
}
public void setLinks(List<Link> links) {
this.links = links;
}
public void setName(String name) {
this.name = name;
}
public void setType(NotificationMethodType type) {
this.type = type;
}
}

View File

@ -1,50 +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 monasca.api.domain.model.notificationmethod;
import java.util.List;
import monasca.api.domain.exception.EntityNotFoundException;
/**
* Repository for notification methods.
*/
public interface NotificationMethodRepo {
NotificationMethod create(String tenantId, String name, NotificationMethodType type,
String address);
/**
* @throws EntityNotFoundException if a notification method cannot be found for the
* {@code notificationMethodId}
*/
void deleteById(String tenantId, String notificationMethodId);
/** Returns whether the {@code notificationMethodId} exists for the {@code tenantId}. */
boolean exists(String tenantId, String notificationMethodId);
/**
* @throws EntityNotFoundException if a notification method cannot be found for the
* {@code notificationMethodId}
*/
NotificationMethod findById(String tenantId, String notificationMethodId);
/**
* @throws EntityNotFoundException if a notification method cannot be found for the
* {@code notificationMethodId}
*/
NotificationMethod update(String tenantId, String notificationMethodId, String name,
NotificationMethodType type, String address);
List<NotificationMethod> find(String tenantId, String offset, int limit);
}

View File

@ -1,25 +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 monasca.api.domain.model.notificationmethod;
import com.fasterxml.jackson.annotation.JsonCreator;
public enum NotificationMethodType {
EMAIL, WEBHOOK, PAGERDUTY;
@JsonCreator
public static NotificationMethodType fromJson(String text) {
return valueOf(text.toUpperCase());
}
}

View File

@ -1,35 +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 monasca.api.domain.model.statistic;
import org.joda.time.DateTime;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Repository for statistics.
*/
public interface StatisticRepo {
/**
* Finds statistics for the given criteria.
*/
List<Statistics> find(String tenantId, String name, Map<String, String> dimensions,
DateTime startTime, @Nullable DateTime endTime, List<String> statistics,
int period, String offset, int limit, Boolean mergeMetricsFlag)
throws Exception;
}

View File

@ -1,138 +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 monasca.api.domain.model.statistic;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import monasca.common.model.domain.common.AbstractEntity;
/**
* Encapsulates a metric measurements.
*/
public class Statistics extends AbstractEntity {
private String name;
private Map<String, String> dimensions;
private List<String> columns;
private List<List<Object>> statistics;
public Statistics() {
statistics = new ArrayList<>();
}
public Statistics(String name, Map<String, String> dimensions, List<String> columns) {
this.name = name;
this.dimensions = dimensions;
this.columns = columns;
this.statistics = new LinkedList<>();
}
public void addValues(List<Object> value) {
statistics.add(value);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Statistics other = (Statistics) obj;
if (dimensions == null) {
if (other.dimensions != null)
return false;
} else if (!dimensions.equals(other.dimensions))
return false;
if (columns == null) {
if (other.columns != null)
return false;
} else if (!columns.equals(other.columns))
return false;
if (statistics == null) {
if (other.statistics != null)
return false;
} else if (!statistics.equals(other.statistics))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public void setId(String id) {
this.id = id;
}
public void addStatistics(List<Object> statistics) {
this.statistics.add(statistics);
}
public List<String> getColumns() {
return columns;
}
public Map<String, String> getDimensions() {
return dimensions;
}
public String getName() {
return name;
}
public List<List<Object>> getStatistics() {
return statistics;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dimensions == null) ? 0 : dimensions.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((statistics == null) ? 0 : statistics.hashCode());
result = prime * result + ((columns == null) ? 0 : columns.hashCode());
return result;
}
public void setColumns(List<String> columns) {
this.columns = columns;
}
public void setDimensions(Map<String, String> dimensions) {
this.dimensions = dimensions;
}
public void setName(String name) {
this.name = name;
}
public void setStatistics(List<List<Object>> statistics) {
this.statistics = statistics;
}
@Override
public String toString() {
return String.format("Statistics [name=%s, dimensions=%s,statistics=%s]", name, dimensions,
statistics);
}
}

View File

@ -1,54 +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 monasca.api.domain.model.version;
import java.util.List;
import org.joda.time.DateTime;
import monasca.common.model.domain.common.AbstractEntity;
import monasca.api.domain.model.common.Link;
import monasca.api.domain.model.common.Linked;
public class Version extends AbstractEntity implements Linked {
private List<Link> links;
public VersionStatus status;
public DateTime updated;
public enum VersionStatus {
CURRENT, DEPRECATED, OBSOLETE;
}
public Version() {}
public Version(String id, VersionStatus status, DateTime updated) {
this.id = id;
this.status = status;
this.updated = updated;
}
public String getId() {
return id;
}
@Override
public List<Link> getLinks() {
return links;
}
@Override
public void setLinks(List<Link> links) {
this.links = links;
}
}

View File

@ -1,30 +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 monasca.api.domain.model.version;
import monasca.api.domain.exception.EntityNotFoundException;
import java.util.List;
/**
* Repository for versions.
*/
public interface VersionRepo {
List<Version> find();
/**
* @throws EntityNotFoundException a version cannot be found for the {@code versionId}
*/
Version findById(String versionId);
}

View File

@ -1,18 +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.
*/
/**
* Houses the domain layer.
*/
package monasca.api.domain;

View File

@ -1,45 +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 monasca.api.domain.service.impl;
import java.util.Arrays;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import monasca.api.domain.exception.EntityNotFoundException;
import monasca.api.domain.model.version.Version;
import monasca.api.domain.model.version.Version.VersionStatus;
import monasca.api.domain.model.version.VersionRepo;
/**
* Version repository implementation.
*/
public class VersionRepoImpl implements VersionRepo {
private static final Version v2_0 = new Version("v2.0", VersionStatus.CURRENT, new DateTime(
DateTimeZone.UTC));
@Override
public List<Version> find() {
return Arrays.asList(v2_0);
}
@Override
public Version findById(String versionId) {
if ("v2.0".equals(versionId))
return v2_0;
throw new EntityNotFoundException("No version exists for %s", versionId);
}
}

View File

@ -1,118 +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 monasca.api.infrastructure;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
import org.influxdb.InfluxDB;
import org.influxdb.InfluxDBFactory;
import javax.inject.Singleton;
import monasca.api.ApiConfig;
import monasca.api.domain.model.alarm.AlarmRepo;
import monasca.api.domain.model.alarmdefinition.AlarmDefinitionRepo;
import monasca.api.domain.model.alarmstatehistory.AlarmStateHistoryRepo;
import monasca.api.domain.model.measurement.MeasurementRepo;
import monasca.api.domain.model.metric.MetricDefinitionRepo;
import monasca.api.domain.model.notificationmethod.NotificationMethodRepo;
import monasca.api.domain.model.statistic.StatisticRepo;
import monasca.api.infrastructure.persistence.PersistUtils;
import monasca.api.infrastructure.persistence.influxdb.InfluxV9AlarmStateHistoryRepo;
import monasca.api.infrastructure.persistence.influxdb.InfluxV9MeasurementRepo;
import monasca.api.infrastructure.persistence.influxdb.InfluxV9MetricDefinitionRepo;
import monasca.api.infrastructure.persistence.influxdb.InfluxV9RepoReader;
import monasca.api.infrastructure.persistence.influxdb.InfluxV9StatisticRepo;
import monasca.api.infrastructure.persistence.influxdb.InfluxV9Utils;
import monasca.api.infrastructure.persistence.mysql.AlarmDefinitionMySqlRepoImpl;
import monasca.api.infrastructure.persistence.mysql.AlarmMySqlRepoImpl;
import monasca.api.infrastructure.persistence.mysql.NotificationMethodMySqlRepoImpl;
import monasca.api.infrastructure.persistence.vertica.AlarmStateHistoryVerticaRepoImpl;
import monasca.api.infrastructure.persistence.vertica.MeasurementVerticaRepoImpl;
import monasca.api.infrastructure.persistence.vertica.MetricDefinitionVerticaRepoImpl;
import monasca.api.infrastructure.persistence.vertica.StatisticVerticaRepoImpl;
/**
* Infrastructure layer bindings.
*/
public class InfrastructureModule extends AbstractModule {
private ApiConfig config;
private static final String VERTICA = "vertica";
private static final String INFLUXDB = "influxdb";
private static final String INFLUXDB_V9 = "v9";
public InfrastructureModule(ApiConfig config) {
this.config = config;
}
@Override
protected void configure() {
// Bind repositories
bind(AlarmRepo.class).to(AlarmMySqlRepoImpl.class).in(Singleton.class);
bind(AlarmDefinitionRepo.class).to(AlarmDefinitionMySqlRepoImpl.class).in(
Singleton.class);
if (config.databaseConfiguration.getDatabaseType().trim().equalsIgnoreCase(VERTICA)) {
bind(AlarmStateHistoryRepo.class).to(AlarmStateHistoryVerticaRepoImpl.class).in(
Singleton.class);
bind(MetricDefinitionRepo.class).to(MetricDefinitionVerticaRepoImpl.class).in(
Singleton.class);
bind(MeasurementRepo.class).to(MeasurementVerticaRepoImpl.class).in(
Singleton.class);
bind(StatisticRepo.class).to(StatisticVerticaRepoImpl.class).in(Singleton.class);
} else if (config.databaseConfiguration.getDatabaseType().trim().equalsIgnoreCase(INFLUXDB)) {
if (config.influxDB.getVersion() != null
&& !config.influxDB.getVersion().equalsIgnoreCase(INFLUXDB_V9)) {
System.err.println("Found unsupported Influxdb version: " + config.influxDB.getVersion());
System.err.println("Supported Influxdb versions are 'v9'");
System.err.println("Check your config file");
System.exit(1);
}
bind(PersistUtils.class).in(Singleton.class);
bind(InfluxV9Utils.class).in(Singleton.class);
bind(InfluxV9RepoReader.class).in(Singleton.class);
bind(AlarmStateHistoryRepo.class).to(InfluxV9AlarmStateHistoryRepo.class).in(Singleton.class);
bind(MetricDefinitionRepo.class).to(InfluxV9MetricDefinitionRepo.class).in(Singleton.class);
bind(MeasurementRepo.class).to(InfluxV9MeasurementRepo.class).in(Singleton.class);
bind(StatisticRepo.class).to(InfluxV9StatisticRepo.class).in(Singleton.class);
} else {
throw new ProvisionException("Failed to detect supported database. Supported databases are "
+ "'vertica' and 'influxdb'. Check your config file.");
}
bind(NotificationMethodRepo.class).to(NotificationMethodMySqlRepoImpl.class).in(
Singleton.class);
}
@Provides
InfluxDB provideInfluxDB() {
InfluxDB influxDB =
InfluxDBFactory.connect(this.config.influxDB.getUrl(), this.config.influxDB.getUser(),
this.config.influxDB.getPassword());
return influxDB;
}
}

View File

@ -1,75 +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 monasca.api.infrastructure.middleware;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* CS Middleware configuration.
*/
public class MiddlewareConfiguration {
public Boolean enabled = false;
@JsonProperty
public String serverVIP;
@JsonProperty
public String serverPort;
@JsonProperty
public Boolean useHttps = Boolean.FALSE;
@JsonProperty
public String connTimeout = "500";
@JsonProperty
public Boolean connSSLClientAuth = Boolean.FALSE;
@JsonProperty
public String connPoolMaxActive = "3";
@JsonProperty
public String connPoolMaxIdle = "3";
@JsonProperty
public String connPoolEvictPeriod = "600000";
@JsonProperty
public String connPoolMinIdleTime = "600000";
@JsonProperty
public String connRetryTimes = "2";
@JsonProperty
public String connRetryInterval = "50";
@JsonProperty
public List<String> defaultAuthorizedRoles;
@JsonProperty
public List<String> agentAuthorizedRoles;
@JsonProperty
public String timeToCacheToken = "600";
@JsonProperty
public String adminAuthMethod;
@JsonProperty
public String adminUser;
@JsonProperty
public String adminToken;
@JsonProperty
public String adminPassword;
@JsonProperty
public String adminProjectId = "";
@JsonProperty
public String adminProjectName = "";
@JsonProperty
public String maxTokenCacheSize = "1048576";
@JsonProperty
public String truststore;
@JsonProperty
public String truststorePassword;
@JsonProperty
public String keystore;
@JsonProperty
public String keystorePassword;
}

View File

@ -1,18 +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.
*/
/**
* Houses the infrastructure layer.
*/
package monasca.api.infrastructure;

View File

@ -1,56 +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 monasca.api.infrastructure.persistence;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.skife.jdbi.v2.Query;
/**
* Utilities for building dimension queries.
*
* This class has issues with testing with mockito because bind method on Query class is final.
*/
public final class DimensionQueries {
private DimensionQueries() {}
public static void bindDimensionsToQuery(Query<?> query, Map<String, String> dimensions) {
if (dimensions != null) {
int i = 0;
for (Iterator<Map.Entry<String, String>> it = dimensions.entrySet().iterator(); it.hasNext(); i++) {
Map.Entry<String, String> entry = it.next();
query.bind("dname" + i, entry.getKey());
query.bind("dvalue" + i, entry.getValue());
}
}
}
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;
}
}

View File

@ -1,66 +0,0 @@
/*
* Copyright (c) 2015 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 monasca.api.infrastructure.persistence;
import com.google.inject.Inject;
import monasca.api.ApiConfig;
public class PersistUtils {
private final int maxQueryLimit;
private final int DEFAULT_MAX_QUERY_LIMIT = 10000;
@Inject
public PersistUtils(ApiConfig config) {
this.maxQueryLimit = config.maxQueryLimit;
}
public PersistUtils(int maxQueryLimit) {
this.maxQueryLimit = maxQueryLimit;
}
public PersistUtils() {
this.maxQueryLimit = DEFAULT_MAX_QUERY_LIMIT;
}
public int getLimit(String limit) {
if (limit == null || limit.isEmpty()) {
return this.maxQueryLimit;
}
int limitInt;
try {
limitInt = Integer.parseInt(limit);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(String.format("Found invalid Limit: '%1$s'. Limit must be an integer.", limit));
}
if (limitInt <= this.maxQueryLimit) {
return limitInt;
} else {
return this.maxQueryLimit;
}
}
}

View File

@ -1,42 +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 monasca.api.infrastructure.persistence;
import java.util.Map;
/**
* Utilities for building sub alarm queries.
*/
public final class SubAlarmDefinitionQueries {
private SubAlarmDefinitionQueries() {}
public static String buildJoinClauseFor(Map<String, String> dimensions) {
StringBuilder sbJoin = new StringBuilder();
if (dimensions != null) {
sbJoin = new StringBuilder();
for (int i = 0; i < dimensions.size(); i++) {
sbJoin.append(" inner join sub_alarm_definition_dimension d").append(i).append(" on d").append(i)
.append(".dimension_name = :dname").append(i).append(" and d").append(i)
.append(".value = :dvalue").append(i).append(" and dim.sub_alarm_definition_id = d")
.append(i).append(".sub_alarm_definition_id");
}
}
return sbJoin.toString();
}
}

View File

@ -1,277 +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 monasca.api.infrastructure.persistence.influxdb;
import com.google.inject.Inject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.inject.Named;
import monasca.api.ApiConfig;
import monasca.api.domain.model.alarmstatehistory.AlarmStateHistory;
import monasca.api.domain.model.alarmstatehistory.AlarmStateHistoryRepo;
import monasca.api.infrastructure.persistence.DimensionQueries;
import monasca.common.model.alarm.AlarmState;
import monasca.common.model.alarm.AlarmTransitionSubAlarm;
import monasca.common.model.metric.MetricDefinition;
public class InfluxV9AlarmStateHistoryRepo implements AlarmStateHistoryRepo {
private static final Logger logger = LoggerFactory
.getLogger(InfluxV9AlarmStateHistoryRepo.class);
private final DBI mysql;
private final ApiConfig config;
private final String region;
private final InfluxV9RepoReader influxV9RepoReader;
private final InfluxV9Utils influxV9Utils;
private static final ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper
.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
}
private final SimpleDateFormat simpleDateFormat =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
private final SimpleDateFormat oldSimpleDateFormat =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
private static final TypeReference<List<MetricDefinition>> METRICS_TYPE =
new TypeReference<List<MetricDefinition>>() {};
private static final TypeReference<List<AlarmTransitionSubAlarm>> SUB_ALARMS_TYPE =
new TypeReference<List<AlarmTransitionSubAlarm>>() {};
@Inject
public InfluxV9AlarmStateHistoryRepo(@Named("mysql") DBI mysql,
ApiConfig config,
InfluxV9RepoReader influxV9RepoReader,
InfluxV9Utils influxV9Utils) {
this.mysql = mysql;
this.config = config;
this.region = config.region;
this.influxV9RepoReader = influxV9RepoReader;
this.influxV9Utils = influxV9Utils;
}
@Override
public List<AlarmStateHistory> findById(String tenantId, String alarmId, String offset,
int limit)
throws Exception {
String q = String.format("select alarm_id, metrics, old_state, new_state, "
+ "reason, reason_data, sub_alarms "
+ "from alarm_state_history "
+ "where %1$s %2$s %3$s %4$s",
this.influxV9Utils.publicTenantIdPart(tenantId),
this.influxV9Utils.alarmIdPart(alarmId),
this.influxV9Utils.timeOffsetPart(offset),
this.influxV9Utils.limitPart(limit));
logger.debug("Alarm state history query: {}", q);
String r = this.influxV9RepoReader.read(q);
Series series = this.objectMapper.readValue(r, Series.class);
List<AlarmStateHistory> alarmStateHistoryList = alarmStateHistoryList(series);
logger.debug("Found {} alarm state transitions matching query", alarmStateHistoryList.size());
return alarmStateHistoryList;
}
@Override
public List<AlarmStateHistory> find(String tenantId, Map<String, String> dimensions,
DateTime startTime, @Nullable DateTime endTime,
@Nullable String offset, int limit) throws Exception {
List<String> alarmIdList = findAlarmIds(this.mysql, tenantId, dimensions);
if (alarmIdList == null || alarmIdList.isEmpty()) {
return new ArrayList<>();
}
String q = String.format("select alarm_id, metrics, old_state, new_state, "
+ "reason, reason_data, sub_alarms "
+ "from alarm_state_history "
+ "where %1$s %2$s %3$s %4$s %5$s",
this.influxV9Utils.publicTenantIdPart(tenantId),
this.influxV9Utils.startTimeEndTimePart(startTime, endTime),
this.influxV9Utils.alarmIdsPart(alarmIdList),
this.influxV9Utils.timeOffsetPart(offset),
this.influxV9Utils.limitPart(limit));
logger.debug("Alarm state history list query: {}", q);
String r = this.influxV9RepoReader.read(q);
Series series = this.objectMapper.readValue(r, Series.class);
List<AlarmStateHistory> alarmStateHistoryList = alarmStateHistoryList(series);
logger.debug("Found {} alarm state transitions matching query", alarmStateHistoryList.size());
return alarmStateHistoryList;
}
private List<AlarmStateHistory> alarmStateHistoryList(Series series) {
List<AlarmStateHistory> alarmStateHistoryList = new LinkedList<>();
if (!series.isEmpty()) {
for (Serie serie : series.getSeries()) {
for (String[] values : serie.getValues()) {
AlarmStateHistory alarmStateHistory = new AlarmStateHistory();
Date date;
try {
date = parseTimestamp(values[0]);
} catch (ParseException e) {
logger.error("Failed to parse time", e);
continue;
}
DateTime dateTime = new DateTime(date.getTime(), DateTimeZone.UTC);
alarmStateHistory.setTimestamp(dateTime);
alarmStateHistory.setAlarmId(values[1]);
List<MetricDefinition> metricDefinitionList;
try {
metricDefinitionList = this.objectMapper.readValue(values[2], METRICS_TYPE);
} catch (IOException e) {
logger.error("Failed to parse metrics", e);
continue;
}
alarmStateHistory.setMetrics(metricDefinitionList);
alarmStateHistory.setOldState(AlarmState.valueOf(values[3]));
alarmStateHistory.setNewState(AlarmState.valueOf(values[4]));
alarmStateHistory.setReason(values[5]);
alarmStateHistory.setReasonData(values[6]);
List<AlarmTransitionSubAlarm> subAlarmList;
try {
subAlarmList = this.objectMapper.readValue(values[7], SUB_ALARMS_TYPE);
} catch (IOException e) {
logger.error("Failed to parse sub-alarms", e);
continue;
}
alarmStateHistory.setSubAlarms(subAlarmList);
alarmStateHistoryList.add(alarmStateHistory);
}
}
}
return alarmStateHistoryList;
}
private Date parseTimestamp(String timestampString) throws ParseException {
try {
return this.simpleDateFormat.parse(timestampString);
}
catch (ParseException pe) {
// This extra part is here just to handle dates in the old format of only
// having seconds. This should be removed in a month or so
return this.oldSimpleDateFormat.parse(timestampString);
}
}
public List<String> findAlarmIds(DBI mysql, String tenantId,
Map<String, String> dimensions) {
final String
FIND_ALARMS_SQL = "select distinct a.id "
+ "from alarm as a "
+ "join alarm_definition as ad on a.alarm_definition_id = ad.id "
+ "%s "
+ "where ad.tenant_id = :tenantId and ad.deleted_at is NULL "
+ "order by ad.created_at";
List<String> alarmIdList = null;
try (Handle h = mysql.open()) {
final String sql = String.format(FIND_ALARMS_SQL, buildJoinClauseFor(dimensions));
Query<Map<String, Object>> query = h.createQuery(sql).bind("tenantId", tenantId);
logger.debug("AlarmStateHistory query '{}'", sql);
DimensionQueries.bindDimensionsToQuery(query, dimensions);
alarmIdList = query.map(StringMapper.FIRST).list();
}
return alarmIdList;
}
private String buildJoinClauseFor(Map<String, String> dimensions) {
if ((dimensions == null) || dimensions.isEmpty()) {
return "";
}
final StringBuilder sbJoin = new StringBuilder("join alarm_metric as am on a.id=am.alarm_id ");
sbJoin.append(
"join metric_definition_dimensions as mdd on am.metric_definition_dimensions_id=mdd.id ");
for (int i = 0; i < dimensions.size(); i++) {
final String tableAlias = "md" + i;
sbJoin.append(" inner join metric_dimension ").append(tableAlias).append(" on ")
.append(tableAlias).append(".name = :dname").append(i).append(" and ").append(tableAlias)
.append(".value = :dvalue").append(i).append(" and mdd.metric_dimension_set_id = ")
.append(tableAlias).append(".dimension_set_id");
}
return sbJoin.toString();
}
}

View File

@ -1,178 +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 monasca.api.infrastructure.persistence.influxdb;
import com.google.inject.Inject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import monasca.api.ApiConfig;
import monasca.api.domain.exception.MultipleMetricsException;
import monasca.api.domain.model.measurement.MeasurementRepo;
import monasca.api.domain.model.measurement.Measurements;
public class InfluxV9MeasurementRepo implements MeasurementRepo {
private static final Logger logger = LoggerFactory
.getLogger(InfluxV9MeasurementRepo.class);
private final static TypeReference VALUE_META_TYPE = new TypeReference<Map<String, String>>() {};
private final ApiConfig config;
private final String region;
private final InfluxV9RepoReader influxV9RepoReader;
private final InfluxV9Utils influxV9Utils;
private final InfluxV9MetricDefinitionRepo influxV9MetricDefinitionRepo;
private final ObjectMapper objectMapper = new ObjectMapper();
@Inject
public InfluxV9MeasurementRepo(ApiConfig config,
InfluxV9RepoReader influxV9RepoReader,
InfluxV9Utils influxV9Utils,
InfluxV9MetricDefinitionRepo influxV9MetricDefinitionRepo) {
this.config = config;
this.region = config.region;
this.influxV9RepoReader = influxV9RepoReader;
this.influxV9Utils = influxV9Utils;
this.influxV9MetricDefinitionRepo = influxV9MetricDefinitionRepo;
}
@Override
public List<Measurements> find(String tenantId, String name, Map<String, String> dimensions,
DateTime startTime, @Nullable DateTime endTime,
@Nullable String offset, int limit, Boolean mergeMetricsFlag)
throws Exception {
String q = buildQuery(tenantId, name, dimensions, startTime, endTime,
offset, limit, mergeMetricsFlag);
String r = this.influxV9RepoReader.read(q);
Series series = this.objectMapper.readValue(r, Series.class);
List<Measurements> measurementsList = measurementsList(series);
logger.debug("Found {} metrics matching query", measurementsList.size());
return measurementsList;
}
private String buildQuery(String tenantId, String name, Map<String, String> dimensions,
DateTime startTime, DateTime endTime, String offset, int limit,
Boolean mergeMetricsFlag) throws Exception {
String q;
if (Boolean.TRUE.equals(mergeMetricsFlag)) {
// Had to use * to handle value meta. If we select valueMeta and it does not exist, then error.
q = String.format("select * %1$s "
+ "where %2$s %3$s %4$s %5$s %6$s %7$s %8$s",
this.influxV9Utils.namePart(name, true),
this.influxV9Utils.privateTenantIdPart(tenantId),
this.influxV9Utils.privateRegionPart(this.region),
this.influxV9Utils.startTimePart(startTime),
this.influxV9Utils.dimPart(dimensions),
this.influxV9Utils.endTimePart(endTime),
this.influxV9Utils.timeOffsetPart(offset),
this.influxV9Utils.limitPart(limit));
} else {
if (!this.influxV9MetricDefinitionRepo.isAtMostOneSeries(tenantId, name, dimensions)) {
throw new MultipleMetricsException(name, dimensions);
}
// Had to use * to handle value meta. If we select valueMeta and it does not exist, then error.
q = String.format("select * %1$s "
+ "where %2$s %3$s %4$s %5$s %6$s %7$s %8$s %9$s slimit 1",
this.influxV9Utils.namePart(name, true),
this.influxV9Utils.privateTenantIdPart(tenantId),
this.influxV9Utils.privateRegionPart(this.region),
this.influxV9Utils.startTimePart(startTime),
this.influxV9Utils.dimPart(dimensions),
this.influxV9Utils.endTimePart(endTime),
this.influxV9Utils.timeOffsetPart(offset),
this.influxV9Utils.groupByPart(),
this.influxV9Utils.limitPart(limit));
}
logger.debug("Measurements query: {}", q);
return q;
}
private List<Measurements> measurementsList(Series series) {
List<Measurements> measurementsList = new LinkedList<>();
if (!series.isEmpty()) {
for (Serie serie : series.getSeries()) {
Measurements measurements =
new Measurements(serie.getName(),
influxV9Utils.filterPrivateTags(serie.getTags()));
for (String[] values : serie.getValues()) {
measurements.addMeasurement(
new Object[]{values[0], Double.parseDouble(values[1]), getValueMeta(values)});
}
measurementsList.add(measurements);
}
}
return measurementsList;
}
private Map<String, String> getValueMeta(String[] values) {
Map<String, String> valueMeta = new HashMap();
if (values.length >= 3 && values[2] != null && !values[2].isEmpty()) {
try {
valueMeta =
this.objectMapper.readValue(values[2], VALUE_META_TYPE);
} catch (IOException e) {
logger.error("Failed to parse value metadata: {}", values[2], e);
}
}
return valueMeta;
}
}

View File

@ -1,199 +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 monasca.api.infrastructure.persistence.influxdb;
import com.google.inject.Inject;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import monasca.api.ApiConfig;
import monasca.api.domain.model.metric.MetricDefinitionRepo;
import monasca.api.domain.model.metric.MetricName;
import monasca.common.model.metric.MetricDefinition;
public class InfluxV9MetricDefinitionRepo implements MetricDefinitionRepo {
private static final Logger logger = LoggerFactory.getLogger(InfluxV9MetricDefinitionRepo.class);
private final ApiConfig config;
private final InfluxV9RepoReader influxV9RepoReader;
private final InfluxV9Utils influxV9Utils;
private final String region;
private final ObjectMapper objectMapper = new ObjectMapper();
@Inject
public InfluxV9MetricDefinitionRepo(ApiConfig config,
InfluxV9RepoReader influxV9RepoReader,
InfluxV9Utils influxV9Utils) {
this.config = config;
this.region = config.region;
this.influxV9RepoReader = influxV9RepoReader;
this.influxV9Utils = influxV9Utils;
}
boolean isAtMostOneSeries(String tenantId, String name, Map<String, String> dimensions)
throws Exception {
// Set limit to 2. We only care if we get 0, 1, or 2 results back.
String q = String.format("show series %1$s "
+ "where %2$s %3$s %4$s limit 2",
this.influxV9Utils.namePart(name, false),
this.influxV9Utils.privateTenantIdPart(tenantId),
this.influxV9Utils.privateRegionPart(this.region),
this.influxV9Utils.dimPart(dimensions));
logger.debug("Metric definition query: {}", q);
String r = this.influxV9RepoReader.read(q);
Series series = this.objectMapper.readValue(r, Series.class);
List<MetricDefinition> metricDefinitionList = metricDefinitionList(series, 0);
logger.debug("Found {} metric definitions matching query", metricDefinitionList.size());
return metricDefinitionList.size() > 1 ? false : true;
}
@Override
public List<MetricDefinition> find(String tenantId, String name,
Map<String, String> dimensions,
String offset, int limit) throws Exception {
int startIndex = this.influxV9Utils.startIndex(offset);
String q = String.format("show series %1$s "
+ "where %2$s %3$s %4$s %5$s %6$s",
this.influxV9Utils.namePart(name, false),
this.influxV9Utils.privateTenantIdPart(tenantId),
this.influxV9Utils.privateRegionPart(this.region),
this.influxV9Utils.dimPart(dimensions),
this.influxV9Utils.limitPart(limit),
this.influxV9Utils.offsetPart(startIndex));
logger.debug("Metric definition query: {}", q);
String r = this.influxV9RepoReader.read(q);
Series series = this.objectMapper.readValue(r, Series.class);
List<MetricDefinition> metricDefinitionList = metricDefinitionList(series, startIndex);
logger.debug("Found {} metric definitions matching query", metricDefinitionList.size());
return metricDefinitionList;
}
@Override
public List<MetricName> findNames(String tenantId, Map<String, String> dimensions,
String offset, int limit) throws Exception {
int startIndex = this.influxV9Utils.startIndex(offset);
String q = String.format("show measurements "
+ "where %1$s %2$s %3$s %4$s %5$s",
this.influxV9Utils.privateTenantIdPart(tenantId),
this.influxV9Utils.privateRegionPart(this.region),
this.influxV9Utils.dimPart(dimensions),
this.influxV9Utils.limitPart(limit),
this.influxV9Utils.offsetPart(startIndex));
logger.debug("Metric name query: {}", q);
String r = this.influxV9RepoReader.read(q);
Series series = this.objectMapper.readValue(r, Series.class);
List<MetricName> metricNameList = metricNameList(series, startIndex);
logger.debug("Found {} metric definitions matching query", metricNameList.size());
return metricNameList;
}
private List<MetricDefinition> metricDefinitionList(Series series, int startIndex) {
List<MetricDefinition> metricDefinitionList = new ArrayList<>();
if (!series.isEmpty()) {
int index = startIndex;
for (Serie serie : series.getSeries()) {
for (String[] values : serie.getValues()) {
MetricDefinition m = new MetricDefinition(serie.getName(), dims(values, serie.getColumns()));
m.setId(String.valueOf(index++));
metricDefinitionList.add(m);
}
}
}
return metricDefinitionList;
}
private List<MetricName> metricNameList(Series series, int startIndex) {
List<MetricName> metricNameList = new ArrayList<>();
if (!series.isEmpty()) {
int index = startIndex;
Serie serie = series.getSeries()[0];
for (String[] values : serie.getValues()) {
MetricName m =
new MetricName(String.valueOf(index++), values[0]);
metricNameList.add(m);
}
}
return metricNameList;
}
private Map<String, String> dims(String[] vals, String[] cols) {
Map<String, String> dims = new HashMap<>();
for (int i = 0; i < cols.length; ++i) {
if (!cols[i].equals("_region")
&& !cols[i].equals("_tenant_id")
&& !cols[i].equals("_id")) {
if (!vals[i].equalsIgnoreCase("null")) {
dims.put(cols[i], vals[i]);
}
}
}
return dims;
}
}

View File

@ -1,168 +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 monasca.api.infrastructure.persistence.influxdb;
import com.google.inject.Inject;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.client.entity.GzipDecompressingEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URLEncoder;
import monasca.api.ApiConfig;
public class InfluxV9RepoReader {
private static final Logger logger = LoggerFactory.getLogger(InfluxV9RepoReader.class);
private final String influxName;
private final String influxUrl;
private final String influxCreds;
private final String influxUser;
private final String influxPass;
private final String baseAuthHeader;
private final boolean gzip;
private final CloseableHttpClient httpClient;
@Inject
public InfluxV9RepoReader(final ApiConfig config) {
this.influxName = config.influxDB.getName();
logger.debug("Influxdb database name: {}", this.influxName);
this.influxUrl = config.influxDB.getUrl() + "/query";
logger.debug("Influxdb URL: {}", this.influxUrl);
this.influxUser = config.influxDB.getUser();
this.influxPass = config.influxDB.getPassword();
this.influxCreds = this.influxUser + ":" + this.influxPass;
this.gzip = config.influxDB.getGzip();
logger.debug("Influxdb gzip responses: {}", this.gzip);
logger.debug("Setting up basic Base64 authentication");
this.baseAuthHeader = "Basic " + new String(Base64.encodeBase64(this.influxCreds.getBytes()));
// We inject InfluxV9RepoReader as a singleton. So, we must share connections safely.
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(config.influxDB.getMaxHttpConnections());
if (this.gzip) {
logger.debug("Setting up gzip responses from Influxdb");
this.httpClient =
HttpClients.custom().setConnectionManager(cm)
.addInterceptorFirst(new HttpRequestInterceptor() {
public void process(final HttpRequest request, final HttpContext context)
throws HttpException, IOException {
if (!request.containsHeader("Accept-Encoding")) {
request.addHeader("Accept-Encoding", "gzip");
}
}
}).addInterceptorFirst(new HttpResponseInterceptor() {
public void process(final HttpResponse response, final HttpContext context)
throws HttpException, IOException {
HttpEntity entity = response.getEntity();
if (entity != null) {
Header ceheader = entity.getContentEncoding();
if (ceheader != null) {
HeaderElement[] codecs = ceheader.getElements();
for (int i = 0; i < codecs.length; i++) {
if (codecs[i].getName().equalsIgnoreCase("gzip")) {
response.setEntity(new GzipDecompressingEntity(response.getEntity()));
return;
}
}
}
}
}
}).build();
} else {
logger.debug("Setting up non-gzip responses from Influxdb");
this.httpClient = HttpClients.custom().setConnectionManager(cm).build();
}
}
protected String read(final String query) throws Exception {
HttpGet request = new HttpGet(this.influxUrl + "?q=" + URLEncoder.encode(query, "UTF-8")
+ "&db=" + URLEncoder.encode(this.influxName, "UTF-8"));
request.addHeader("content-type", "application/json");
request.addHeader("Authorization", this.baseAuthHeader);
try {
logger.debug("Sending query {} to influx database {} at {}", query, this.influxName,
this.influxUrl);
HttpResponse response = this.httpClient.execute(request);
int rc = response.getStatusLine().getStatusCode();
logger.debug("Received {} status code from influx database {} at {}", rc, this.influxName,
this.influxUrl);
if (rc != HttpStatus.SC_OK) {
HttpEntity entity = response.getEntity();
String responseString = EntityUtils.toString(entity, "UTF-8");
logger
.error("Failed to query influx database {} at {}: {}", this.influxName, this.influxUrl,
String.valueOf(rc));
logger.error("Http response: {}", responseString);
throw new Exception(rc + ":" + responseString);
}
logger
.debug("Successfully queried influx database {} at {}", this.influxName, this.influxUrl);
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity, "UTF-8") : null;
} finally {
request.releaseConnection();
}
}
}

View File

@ -1,207 +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 monasca.api.infrastructure.persistence.influxdb;
import com.google.inject.Inject;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import monasca.api.ApiConfig;
import monasca.api.domain.exception.MultipleMetricsException;
import monasca.api.domain.model.statistic.StatisticRepo;
import monasca.api.domain.model.statistic.Statistics;
public class InfluxV9StatisticRepo implements StatisticRepo {
private static final Logger logger = LoggerFactory.getLogger(InfluxV9StatisticRepo.class);
private final ApiConfig config;
private final String region;
private final InfluxV9RepoReader influxV9RepoReader;
private final InfluxV9Utils influxV9Utils;
private final InfluxV9MetricDefinitionRepo influxV9MetricDefinitionRepo;
private final ObjectMapper objectMapper = new ObjectMapper();
@Inject
public InfluxV9StatisticRepo(ApiConfig config,
InfluxV9RepoReader influxV9RepoReader,
InfluxV9Utils influxV9Utils,
InfluxV9MetricDefinitionRepo influxV9MetricDefinitionRepo) {
this.config = config;
this.region = config.region;
this.influxV9RepoReader = influxV9RepoReader;
this.influxV9Utils = influxV9Utils;
this.influxV9MetricDefinitionRepo = influxV9MetricDefinitionRepo;
}
@Override
public List<Statistics> find(String tenantId, String name, Map<String, String> dimensions,
DateTime startTime, @Nullable DateTime endTime,
List<String> statistics, int period, String offset, int limit,
Boolean mergeMetricsFlag) throws Exception {
String q = buildQuery(tenantId, name, dimensions, startTime, endTime,
statistics, period, offset, limit, mergeMetricsFlag);
String r = this.influxV9RepoReader.read(q);
Series series = this.objectMapper.readValue(r, Series.class);
List<Statistics> statisticsList = statisticslist(series);
logger.debug("Found {} metric definitions matching query", statisticsList.size());
return statisticsList;
}
private String buildQuery(String tenantId, String name, Map<String, String> dimensions,
DateTime startTime, DateTime endTime, List<String> statistics,
int period, String offset, int limit, Boolean mergeMetricsFlag)
throws Exception {
String q;
if (Boolean.TRUE.equals(mergeMetricsFlag)) {
q = String.format("select %1$s %2$s "
+ "where %3$s %4$s %5$s %6$s %7$s %8$s %9$s %10$s",
funcPart(statistics),
this.influxV9Utils.namePart(name, true),
this.influxV9Utils.privateTenantIdPart(tenantId),
this.influxV9Utils.privateRegionPart(this.region),
this.influxV9Utils.startTimePart(startTime),
this.influxV9Utils.dimPart(dimensions),
this.influxV9Utils.endTimePart(endTime),
this.influxV9Utils.timeOffsetPart(offset),
this.influxV9Utils.periodPart(period),
this.influxV9Utils.limitPart(limit));
} else {
if (!this.influxV9MetricDefinitionRepo.isAtMostOneSeries(tenantId, name, dimensions)) {
throw new MultipleMetricsException(name, dimensions);
}
q = String.format("select %1$s %2$s "
+ "where %3$s %4$s %5$s %6$s %7$s %8$s %9$s %10$s slimit 1",
funcPart(statistics),
this.influxV9Utils.namePart(name, true),
this.influxV9Utils.privateTenantIdPart(tenantId),
this.influxV9Utils.privateRegionPart(this.region),
this.influxV9Utils.startTimePart(startTime),
this.influxV9Utils.dimPart(dimensions),
this.influxV9Utils.endTimePart(endTime),
this.influxV9Utils.timeOffsetPart(offset),
this.influxV9Utils.periodPartWithGroupBy(period),
this.influxV9Utils.limitPart(limit));
}
logger.debug("Statistics query: {}", q);
return q;
}
private List<Statistics> statisticslist(Series series) {
List<Statistics> statisticsList = new LinkedList<>();
if (!series.isEmpty()) {
for (Serie serie : series.getSeries()) {
Statistics statistics = new Statistics(serie.getName(),
this.influxV9Utils.filterPrivateTags(serie.getTags()),
Arrays.asList(translateNames(serie.getColumns())));
for (Object[] values : serie.getValues()) {
statistics.addStatistics(buildValsList(values));
}
statisticsList.add(statistics);
}
}
return statisticsList;
}
private List<Object> buildValsList(Object[] values) {
ArrayList<Object> valObjArryList = new ArrayList<>();
// First value is the timestamp.
valObjArryList.add(values[0]);
// All other values are doubles.
for (int i = 1; i < values.length; ++i) {
valObjArryList.add(Double.parseDouble((String) values[i]));
}
return valObjArryList;
}
private String[] translateNames(String[] columnNamesArry) {
for (int i = 0; i < columnNamesArry.length; i++) {
columnNamesArry[i] = columnNamesArry[i].replaceAll("^time$", "timestamp");
columnNamesArry[i] = columnNamesArry[i].replaceAll("^mean$", "avg");
}
return columnNamesArry;
}
private String funcPart(List<String> statistics) {
StringBuilder sb = new StringBuilder();
for (String stat : statistics) {
if (sb.length() != 0) {
sb.append(",");
}
if (stat.trim().toLowerCase().equals("avg")) {
sb.append("mean(value)");
} else {
sb.append(String.format("%1$s(value)", stat));
}
}
return sb.toString();
}
}

View File

@ -1,236 +0,0 @@
/*
* Copyright (c) 2015 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 monasca.api.infrastructure.persistence.influxdb;
import com.google.inject.Inject;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import monasca.api.infrastructure.persistence.PersistUtils;
public class InfluxV9Utils {
private final PersistUtils persistUtils;
private static final Pattern sqlUnsafePattern = Pattern.compile("^.*('|;|\")+.*$");
@Inject
public InfluxV9Utils(PersistUtils persistUtils) {
this.persistUtils = persistUtils;
}
public String sanitize(final String taintedString) throws Exception {
Matcher m = sqlUnsafePattern.matcher(taintedString);
if (m.matches()) {
throw new Exception(String.format("Input from user contains single quote ['] or "
+ "semi-colon [;] or double quote [\"] characters[ %1$s ]",
taintedString));
}
return taintedString;
}
String buildTimePart(final DateTime startTime, final DateTime endTime) {
final StringBuilder sb = new StringBuilder();
if (startTime != null) {
sb.append(String.format(" and time > %1$ds", startTime.getMillis() / 1000));
}
if (endTime != null) {
sb.append(String.format(" and time < %1$ds", endTime.getMillis() / 1000));
}
return sb.toString();
}
public String buildAlarmsPart(List<String> alarmIds) {
StringBuilder sb = new StringBuilder();
for (String alarmId : alarmIds) {
if (sb.length() > 0) {
sb.append(" or ");
}
sb.append(String.format(" alarm_id = '%1$s' ", alarmId));
}
if (sb.length() > 0) {
sb.insert(0, " and (");
sb.insert(sb.length(), ")");
}
return sb.toString();
}
public String groupByPart() {
return " group by *";
}
public String namePart(String name, boolean isRequired) throws Exception {
if (isRequired) {
if (name == null || name.isEmpty()) {
throw new Exception(String.format("Found null or empty name: %1$s", name));
}
}
if (name == null || name.isEmpty()) {
return "";
} else {
return String.format(" from \"%1$s\"", sanitize(name));
}
}
public String publicTenantIdPart(String tenantId) throws Exception {
if (tenantId == null || tenantId.isEmpty()) {
throw new Exception(String.format("Found null or empty tenant id: %1$s", tenantId));
}
return " tenant_id=" + "'" + sanitize(tenantId) + "'";
}
public String privateTenantIdPart(String tenantId) throws Exception {
if (tenantId == null || tenantId.isEmpty()) {
throw new Exception(String.format("Found null or empty tenant id: %1$s", tenantId));
}
return " _tenant_id=" + "'" + sanitize(tenantId) + "'";
}
public String alarmIdPart(String alarmId) {
if (alarmId == null || alarmId.isEmpty()) {
return "";
}
return " and alarm_id=" + "'" + alarmId + "'";
}
public String timeOffsetPart(String offset) {
if (offset == null || offset.isEmpty()) {
return "";
}
return String.format(" and time > '%1$s'", offset);
}
public String privateRegionPart(String region) throws Exception {
if (region == null || region.isEmpty()) {
throw new Exception(String.format("Found null or empty region: %1$s", region));
}
return " and _region=" + "'" + sanitize(region) + "'";
}
public String dimPart(Map<String, String> dims) throws Exception {
StringBuilder sb = new StringBuilder();
if (dims != null && !dims.isEmpty()) {
for (String k : dims.keySet()) {
String v = dims.get(k);
if (k != null && !k.isEmpty() && v != null && !v.isEmpty()) {
sb.append(" and " + sanitize(k) + "=" + "'" + sanitize(v) + "'");
}
}
}
return sb.toString();
}
public String startTimePart(DateTime startTime) {
return startTime != null ? " and time > " + "'" + ISODateTimeFormat.dateTime().print(startTime)
+ "'" : "";
}
public String endTimePart(DateTime endTime) {
return endTime != null ? " and time < " + "'" + ISODateTimeFormat.dateTime().print(endTime)
+ "'" : "";
}
public String limitPart(int limit) {
// We add 1 to limit to determine if we need to insert a next link.
return String.format(" limit %1$d", limit + 1);
}
public String offsetPart(int startIndex) {
return String.format(" offset %1$d", startIndex);
}
public int startIndex(String offset) {
if (offset == null || offset.isEmpty()) {
return 0;
}
// We've already returned up to offset, so return offset + 1.
return Integer.parseInt(offset) + 1;
}
public String startTimeEndTimePart(DateTime startTime, DateTime endTime) {
return buildTimePart(startTime, endTime);
}
public String alarmIdsPart(List<String> alarmIdList) {
return buildAlarmsPart(alarmIdList);
}
public String periodPartWithGroupBy(int period) {
return period > 0 ? String.format(" group by time(%1$ds), * fill(0)", period)
: " group by time(300s), * fill(0)";
}
public String periodPart(int period) {
return period > 0 ? String.format(" group by time(%1$ds) fill(0)", period)
: " group by time(300s) fill(0)";
}
Map<String, String> filterPrivateTags(Map<String, String> tagMap) {
Map<String, String> filteredMap = new HashMap<>(tagMap);
filteredMap.remove("_tenant_id");
filteredMap.remove("_region");
return filteredMap;
}
}

View File

@ -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 monasca.api.infrastructure.persistence.influxdb;
import java.util.HashMap;
import java.util.Map;
public class Series {
public SeriesElement[] results;
public String error;
boolean isEmpty() {
return this.results[0].series == null;
}
int getSeriesLength() {
if (!isEmpty()) {
return this.results[0].series.length;
} else {
return 0;
}
}
Serie[] getSeries() {
return this.results[0].series;
}
public String getError() {
return this.error;
}
}
class SeriesElement {
public Serie[] series;
public String error;
}
class Serie {
// Initialize to defaults to avoid NPE.
public String name = "";
Map<String, String> tags = new HashMap();
public String[] columns = new String[0];
public String[][] values = new String[0][0];
public String getName() {
return name;
}
public Map getTags() {
return tags;
}
public String[] getColumns() {
return columns;
}
public String[][] getValues() {
return values;
}
}

View File

@ -1,412 +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 monasca.api.infrastructure.persistence.mysql;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.sql.SQLException;
import java.sql.ResultSet;
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.tweak.ResultSetMapper;
import org.skife.jdbi.v2.StatementContext;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import monasca.api.infrastructure.persistence.PersistUtils;
import monasca.common.model.alarm.AggregateFunction;
import monasca.common.model.alarm.AlarmOperator;
import monasca.common.model.alarm.AlarmState;
import monasca.common.model.alarm.AlarmSubExpression;
import monasca.common.model.metric.MetricDefinition;
import monasca.api.domain.exception.EntityNotFoundException;
import monasca.api.domain.model.alarmdefinition.AlarmDefinition;
import monasca.api.domain.model.alarmdefinition.AlarmDefinitionRepo;
import monasca.api.infrastructure.persistence.DimensionQueries;
import monasca.api.infrastructure.persistence.SubAlarmDefinitionQueries;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
/**
* Alarm repository implementation.
*/
public class AlarmDefinitionMySqlRepoImpl implements AlarmDefinitionRepo {
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;
private final PersistUtils persistUtils;
@Inject
public AlarmDefinitionMySqlRepoImpl(@Named("mysql") DBI db, PersistUtils persistUtils) {
this.db = db;
this.persistUtils = persistUtils;
}
@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 String exists(String tenantId, String name) {
try (Handle h = db.open()) {
Map<String, Object> map = h
.createQuery(
"select id from alarm_definition where tenant_id = :tenantId and name = :name and deleted_at is NULL")
.bind("tenantId", tenantId).bind("name", name).first();
if (map != null) {
if (map.values().size() != 0) {
return map.get("id").toString();
} else {
return null;
}
} else {
return null;
}
}
}
@SuppressWarnings("unchecked")
@Override
public List<AlarmDefinition> find(String tenantId, String name,
Map<String, String> dimensions, String offset, int limit) {
try (Handle h = db.open()) {
String query =
" SELECT t.id, t.tenant_id, t.name, t.description, t.expression, t.severity, t.match_by,"
+ " t.actions_enabled, t.created_at, t.updated_at, t.deleted_at, "
+ " GROUP_CONCAT(aa.alarm_state) AS states, "
+ " GROUP_CONCAT(aa.action_id) AS notificationIds "
+ "FROM (SELECT distinct ad.id, ad.tenant_id, ad.name, ad.description, ad.expression,"
+ " ad.severity, ad.match_by, ad.actions_enabled, ad.created_at, "
+ " ad.updated_at, ad.deleted_at "
+ " FROM alarm_definition AS ad "
+ " LEFT OUTER JOIN sub_alarm_definition AS sad ON ad.id = sad.alarm_definition_id "
+ " LEFT OUTER JOIN sub_alarm_definition_dimension AS dim ON sad.id = dim.sub_alarm_definition_id %1$s "
+ " WHERE ad.tenant_id = :tenantId AND ad.deleted_at IS NULL %2$s %3$s) AS t "
+ "LEFT OUTER JOIN alarm_action AS aa ON t.id = aa.alarm_definition_id "
+ "GROUP BY t.id ORDER BY t.id, t.created_at";
StringBuilder sbWhere = new StringBuilder();
if (name != null) {
sbWhere.append(" and ad.name = :name");
}
if (offset != null) {
sbWhere.append(" and ad.id > :offset");
}
String limitPart = "";
if (limit > 0) {
limitPart = " limit :limit";
}
String sql = String.format(query,
SubAlarmDefinitionQueries.buildJoinClauseFor(dimensions), sbWhere, limitPart);
Query<?> q = h.createQuery(sql);
q.bind("tenantId", tenantId);
if (name != null) {
q.bind("name", name);
}
if (offset != null) {
q.bind("offset", offset);
}
if (limit > 0) {
q.bind("limit", limit + 1);
}
q.registerMapper(new AlarmDefinitionMapper());
q = q.mapTo(AlarmDefinition.class);
DimensionQueries.bindDimensionsToQuery(q, dimensions);
List<AlarmDefinition> resultSet = (List<AlarmDefinition>) q.list();
return resultSet;
}
}
@Override
public AlarmDefinition findById(String tenantId, String alarmDefId) {
try (Handle h = db.open()) {
String query = "SELECT alarm_definition.id, alarm_definition.tenant_id, alarm_definition.name, alarm_definition.description, "
+ "alarm_definition.expression, alarm_definition.severity, alarm_definition.match_by, alarm_definition.actions_enabled, "
+" alarm_definition.created_at, alarm_definition.updated_at, alarm_definition.deleted_at, "
+ "GROUP_CONCAT(alarm_action.action_id) AS notificationIds,group_concat(alarm_action.alarm_state) AS states "
+ "FROM alarm_definition LEFT OUTER JOIN alarm_action ON alarm_definition.id=alarm_action.alarm_definition_id "
+ " WHERE alarm_definition.tenant_id=:tenantId AND alarm_definition.id=:alarmDefId AND alarm_definition.deleted_at "
+ " IS NULL GROUP BY alarm_definition.id";
Query<?> q = h.createQuery(query);
q.bind("tenantId", tenantId);
q.bind("alarmDefId", alarmDefId);
q.registerMapper(new AlarmDefinitionMapper());
q = q.mapTo(AlarmDefinition.class);
AlarmDefinition alarmDefinition = (AlarmDefinition) q.first();
if(alarmDefinition == null)
{
throw new EntityNotFoundException("No alarm definition exists for %s", alarmDefId);
}
return alarmDefinition;
}
}
@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_definition_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_definition_id = ? and alarm_state = ?", id,
alarmState.name());
}
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 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());
}
}
}
private static class AlarmDefinitionMapper implements ResultSetMapper<AlarmDefinition> {
private static final Splitter
COMMA_SPLITTER =
Splitter.on(',').omitEmptyStrings().trimResults();
public AlarmDefinition map(int index, ResultSet r, StatementContext ctx) throws SQLException {
String notificationIds = r.getString("notificationIds");
String states = r.getString("states");
String matchBy = r.getString("match_by");
List<String> notifications = splitStringIntoList(notificationIds);
List<String> state = splitStringIntoList(states);
List<String> match = splitStringIntoList(matchBy);
List<String> okActionIds = new ArrayList<String>();
List<String> alarmActionIds = new ArrayList<String>();
List<String> undeterminedActionIds = new ArrayList<String>();
int stateAndActionIndex = 0;
for (String singleState : state) {
if (singleState.equals(AlarmState.UNDETERMINED.name())) {
undeterminedActionIds.add(notifications.get(stateAndActionIndex));
}
if (singleState.equals(AlarmState.OK.name())) {
okActionIds.add(notifications.get(stateAndActionIndex));
}
if (singleState.equals(AlarmState.ALARM.name())) {
alarmActionIds.add(notifications.get(stateAndActionIndex));
}
stateAndActionIndex++;
}
return new AlarmDefinition(r.getString("id"), r.getString("name"), r.getString("description"),
r.getString("severity"), r.getString("expression"), match,
r.getBoolean("actions_enabled"), alarmActionIds, okActionIds,
undeterminedActionIds);
}
private List<String> splitStringIntoList(String commaDelimitedString) {
if (commaDelimitedString == null) {
return new ArrayList<String>();
}
Iterable<String> split = COMMA_SPLITTER.split(commaDelimitedString);
return Lists.newArrayList(split);
}
}
}

View File

@ -1,331 +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 monasca.api.infrastructure.persistence.mysql;
import monasca.api.domain.exception.EntityNotFoundException;
import monasca.api.domain.model.alarm.Alarm;
import monasca.api.domain.model.alarm.AlarmRepo;
import monasca.api.infrastructure.persistence.DimensionQueries;
import monasca.api.infrastructure.persistence.PersistUtils;
import monasca.common.model.alarm.AlarmState;
import monasca.common.model.alarm.AlarmSubExpression;
import monasca.common.model.metric.MetricDefinition;
import monasca.common.persistence.BeanMapper;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
/**
* Alarmed metric repository implementation.
*/
public class AlarmMySqlRepoImpl implements AlarmRepo {
private static final Logger logger = LoggerFactory.getLogger(AlarmMySqlRepoImpl.class);
private final DBI db;
private final PersistUtils persistUtils;
private static final String ALARM_SQL =
"select ad.id as alarm_definition_id, ad.severity, ad.name as alarm_definition_name, "
+ "a.id, a.state, a.updated_at as state_updated_timestamp, a.created_at as created_timestamp,"
+ "md.name as metric_name, mdg.dimensions as metric_dimensions from alarm as a "
+ "inner join alarm_definition ad on ad.id = a.alarm_definition_id "
+ "inner join alarm_metric as am on am.alarm_id = a.id "
+ "inner join metric_definition_dimensions as mdd on mdd.id = am.metric_definition_dimensions_id "
+ "inner join metric_definition as md on md.id = mdd.metric_definition_id "
+ "left join (select dimension_set_id, name, value, 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 ad.tenant_id = :tenantId and ad.deleted_at is null %s order by a.id %s";
@Inject
public AlarmMySqlRepoImpl(@Named("mysql") DBI db, PersistUtils persistUtils) {
this.db = db;
this.persistUtils = persistUtils;
}
private void buildJoinClauseFor(Map<String, String> dimensions, StringBuilder sbJoin) {
if (dimensions == null) {
return;
}
for (int i = 0; i < dimensions.size(); i++) {
final String indexStr = String.valueOf(i);
sbJoin.append(" inner join metric_dimension md").append(indexStr).append(" on md")
.append(indexStr)
.append(".name = :dname").append(indexStr).append(" and md").append(indexStr)
.append(".value = :dvalue").append(indexStr)
.append(" and mdd.metric_dimension_set_id = md")
.append(indexStr).append(".dimension_set_id");
}
}
@Override
public void deleteById(String tenantId, String id) {
final String sql = "delete a from alarm a where a.id = ?";
try (Handle h = db.open()) {
// This will throw an EntityNotFoundException if Alarm doesn't exist or has a different tenant id
findAlarm(tenantId, id, h);
h.execute(sql, id);
}
}
@Override
public List<Alarm> find(String tenantId, String alarmDefId, String metricName,
Map<String, String> metricDimensions, AlarmState state,
DateTime stateUpdatedStart, String offset,
int limit, boolean enforceLimit) {
try (Handle h = db.open()) {
StringBuilder sbWhere = new StringBuilder();
if (alarmDefId != null) {
sbWhere.append("and ad.id = :alarmDefId ");
}
if (metricName != null) {
sbWhere.append(" and a.id in (select distinct a.id from alarm as a "
+ "inner join alarm_metric as am on am.alarm_id = a.id "
+ "inner join metric_definition_dimensions as mdd "
+ " on mdd.id = am.metric_definition_dimensions_id "
+ "inner join (select distinct id from metric_definition "
+ " where name = :metricName) as md "
+ "on md.id = mdd.metric_definition_id ");
buildJoinClauseFor(metricDimensions, sbWhere);
sbWhere.append(")");
}
if (state != null) {
sbWhere.append(" and a.state = :state");
}
if (stateUpdatedStart != null) {
sbWhere.append(" and a.updated_at >= :stateUpdatedStart");
}
if (offset != null) {
sbWhere.append(" and a.id > :offset");
}
String limitPart = "";
if (enforceLimit && limit > 0) {
limitPart = " limit :limit";
}
String sql = String.format(ALARM_SQL, sbWhere, limitPart);
final Query<Map<String, Object>> q = h.createQuery(sql).bind("tenantId", tenantId);
if (alarmDefId != null) {
q.bind("alarmDefId", alarmDefId);
}
if (metricName != null) {
q.bind("metricName", metricName);
}
if (state != null) {
q.bind("state", state.name());
}
if (stateUpdatedStart != null) {
q.bind("stateUpdatedStart", stateUpdatedStart.toString().replace('Z', ' '));
}
if (offset != null) {
q.bind("offset", offset);
}
if (enforceLimit && limit > 0) {
q.bind("limit", limit + 1);
}
DimensionQueries.bindDimensionsToQuery(q, metricDimensions);
final long start = System.currentTimeMillis();
final List<Map<String, Object>> rows = q.list();
logger.debug("Query took {} milliseconds", System.currentTimeMillis() - start);
final List<Alarm> alarms = createAlarms(tenantId, rows);
return alarms;
}
}
@Override
public Alarm findById(String tenantId, String alarmId) {
try (Handle h = db.open()) {
return findAlarm(tenantId, alarmId, h);
}
}
private Alarm findAlarm(String tenantId, String alarmId, Handle h) {
final String sql = String.format(ALARM_SQL, " and a.id = :id", "");
final List<Map<String, Object>> rows =
h.createQuery(sql).bind("id", alarmId).bind("tenantId", tenantId).list();
if (rows.isEmpty()) {
throw new EntityNotFoundException("No alarm exists for %s", alarmId);
}
return createAlarms(tenantId, rows).get(0);
}
private List<Alarm> createAlarms(String tenantId, List<Map<String, Object>> rows) {
Alarm alarm = null;
String previousAlarmId = null;
final List<Alarm> alarms = new LinkedList<>();
List<MetricDefinition> alarmedMetrics = null;
for (final Map<String, Object> row : rows) {
final String alarmId = (String) row.get("id");
if (!alarmId.equals(previousAlarmId)) {
alarmedMetrics = new ArrayList<>();
alarm =
new Alarm(alarmId, getString(row, "alarm_definition_id"), getString(row,
"alarm_definition_name"), getString(row, "severity"), alarmedMetrics,
AlarmState.valueOf(getString(row, "state")),
new DateTime(((Timestamp)row.get("state_updated_timestamp")).getTime(), DateTimeZone.forID("UTC")),
new DateTime(((Timestamp)row.get("created_timestamp")).getTime(), DateTimeZone.forID("UTC")));
alarms.add(alarm);
}
previousAlarmId = alarmId;
final Map<String, String> dimensionMap = new HashMap<>();
// Not all Metrics have dimensions (at least theoretically)
if (row.containsKey("metric_dimensions")) {
final String dimensions = getString(row, "metric_dimensions");
if (dimensions != null && !dimensions.isEmpty()) {
for (String dimension : dimensions.split(",")) {
final String[] parsed_dimension = dimension.split("=");
if (parsed_dimension.length == 2) {
dimensionMap.put(parsed_dimension[0], parsed_dimension[1]);
} else {
logger
.error("Failed to parse dimension. Dimension is malformed: {}", parsed_dimension);
}
}
}
}
alarmedMetrics.add(new MetricDefinition(getString(row, "metric_name"), dimensionMap));
}
return alarms;
}
private String getString(final Map<String, Object> row, String fieldName) {
return (String) row.get(fieldName);
}
@Override
public Alarm update(String tenantId, String id, AlarmState state) {
Handle h = db.open();
try {
h.begin();
final Alarm originalAlarm = findAlarm(tenantId, id, h);
if (!originalAlarm.getState().equals(state)) {
h.insert("update alarm set state = ?, updated_at = NOW() where id = ?", state.name(), id);
}
h.commit();
return originalAlarm;
} catch (RuntimeException e) {
h.rollback();
throw e;
} finally {
h.close();
}
}
public static class SubAlarm {
private String id;
private String expression;
public SubAlarm() {
}
public SubAlarm(String id, String expression) {
this.id = id;
this.expression = expression;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getExpression() {
return expression;
}
public void setExpression(String expression) {
this.expression = expression;
}
}
@Override
public Map<String, AlarmSubExpression> findAlarmSubExpressions(String alarmId) {
try (Handle h = db.open()) {
final List<SubAlarm> result = h
.createQuery("select * from sub_alarm where alarm_id = :alarmId")
.bind("alarmId", alarmId)
.map(new BeanMapper<SubAlarm>(SubAlarm.class)).list();
final Map<String, AlarmSubExpression> subAlarms = new HashMap<>(result.size());
for (SubAlarm row : result) {
subAlarms.put(row.id, AlarmSubExpression.of(row.expression));
}
return subAlarms;
}
}
@Override
public Map<String, Map<String, AlarmSubExpression>> findAlarmSubExpressionsForAlarmDefinition(
String alarmDefinitionId) {
try (Handle h = db.open()) {
final List<Map<String, Object>> rows = h
.createQuery(
"select sa.* from sub_alarm as sa, alarm as a where sa.alarm_id=a.id and a.alarm_definition_id = :alarmDefinitionId")
.bind("alarmDefinitionId", alarmDefinitionId).list();
Map<String, Map<String, AlarmSubExpression>> subAlarms = new HashMap<>();
for (Map<String, Object> row : rows) {
final String alarmId = (String) row.get("alarm_id");
Map<String, AlarmSubExpression> alarmMap = subAlarms.get(alarmId);
if (alarmMap == null) {
alarmMap = new HashMap<>();
subAlarms.put(alarmId, alarmMap);
}
final String id = (String) row.get("id");
final String expression = (String) row.get("expression");
alarmMap.put(id, AlarmSubExpression.of(expression));
}
return subAlarms;
}
}
}

View File

@ -1,182 +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 monasca.api.infrastructure.persistence.mysql;
import java.util.List;
import java.util.Map;
import java.util.UUID;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import monasca.api.domain.exception.EntityExistsException;
import monasca.api.domain.exception.EntityNotFoundException;
import monasca.api.domain.model.notificationmethod.NotificationMethod;
import monasca.api.domain.model.notificationmethod.NotificationMethodRepo;
import monasca.api.domain.model.notificationmethod.NotificationMethodType;
import monasca.api.infrastructure.persistence.PersistUtils;
import monasca.common.persistence.BeanMapper;
/**
* Notification method repository implementation.
*/
public class NotificationMethodMySqlRepoImpl implements NotificationMethodRepo {
private static final Logger LOG = LoggerFactory
.getLogger(NotificationMethodMySqlRepoImpl.class);
private final DBI db;
private final PersistUtils persistUtils;
@Inject
public NotificationMethodMySqlRepoImpl(@Named("mysql") DBI db, PersistUtils persistUtils) {
this.db = db;
this.persistUtils = persistUtils;
}
@Override
public NotificationMethod create(String tenantId, String name,
NotificationMethodType type, String address) {
try (Handle h = db.open()) {
h.begin();
if (getNotificationIdForTenantIdAndName(h,tenantId, name) != null)
throw new EntityExistsException(
"Notification method %s \"%s\" already exists.", tenantId, name);
String id = UUID.randomUUID().toString();
h.insert(
"insert into notification_method (id, tenant_id, name, type, address, created_at, updated_at) values (?, ?, ?, ?, ?, NOW(), NOW())",
id, tenantId, name, type.toString(), address);
LOG.debug("Creating notification method {} for {}", name, tenantId);
h.commit();
return new NotificationMethod(id, name, type, address);
}
}
@Override
public void deleteById(String tenantId, String notificationMethodId) {
try (Handle h = db.open()) {
if (h.update("delete from notification_method where tenant_id = ? and id = ?", tenantId,
notificationMethodId) == 0)
throw new EntityNotFoundException("No notification method exists for %s",
notificationMethodId);
}
}
@Override
public boolean exists(String tenantId, String notificationMethodId) {
try (Handle h = db.open()) {
return h
.createQuery(
"select exists(select 1 from notification_method where tenant_id = :tenantId and id = :notificationMethodId)")
.bind("tenantId", tenantId).bind("notificationMethodId", notificationMethodId)
.mapTo(Boolean.TYPE).first();
}
}
private String getNotificationIdForTenantIdAndName(Handle h,String tenantId, String name) {
Map<String, Object> map = h
.createQuery(
"select id from notification_method where tenant_id = :tenantId and name = :name")
.bind("tenantId", tenantId).bind("name", name).first();
if (map != null && !map.isEmpty()) {
return map.get("id").toString();
}
else {
return null;
}
}
@Override
public List<NotificationMethod> find(String tenantId, String offset, int limit) {
try (Handle h = db.open()) {
String rawQuery =
" SELECT nm.id, nm.tenant_id, nm.name, nm.type, nm.address, nm.created_at, nm.updated_at "
+ "FROM notification_method as nm "
+ "WHERE tenant_id = :tenantId %1$s order by nm.id asc %2$s";
String offsetPart = "";
if (offset != null) {
offsetPart = "and nm.id > :offset";
}
String limitPart = "";
if (limit > 0) {
limitPart = " limit :limit";
}
String query = String.format(rawQuery, offsetPart, limitPart);
Query<?> q = h.createQuery(query);
q.bind("tenantId", tenantId);
if (offset != null) {
q.bind("offset", offset);
}
if (limit > 0) {
q.bind("limit", limit + 1);
}
return q.map(new BeanMapper<>(NotificationMethod.class)).list();
}
}
@Override
public NotificationMethod findById(String tenantId, String notificationMethodId) {
try (Handle h = db.open()) {
NotificationMethod notificationMethod =
h.createQuery(
"select * from notification_method where tenant_id = :tenantId and id = :id")
.bind("tenantId", tenantId).bind("id", notificationMethodId)
.map(new BeanMapper<NotificationMethod>(NotificationMethod.class)).first();
if (notificationMethod == null)
throw new EntityNotFoundException("No notification method exists for %s",
notificationMethodId);
return notificationMethod;
}
}
@Override
public NotificationMethod update(String tenantId, String notificationMethodId, String name,
NotificationMethodType type, String address) {
try (Handle h = db.open()) {
h.begin();
String notificationID = getNotificationIdForTenantIdAndName(h,tenantId, name);
if (notificationID != null && !notificationID.equalsIgnoreCase(notificationMethodId)) {
throw new EntityExistsException("Notification method %s \"%s\" already exists.",
tenantId, name);
}
if (h
.update(
"update notification_method set name = ?, type = ?, address = ? where tenant_id = ? and id = ?",
name, type.name(), address, tenantId, notificationMethodId) == 0)
throw new EntityNotFoundException("No notification method exists for %s",
notificationMethodId);
h.commit();
return new NotificationMethod(notificationMethodId, name, type, address);
}
}
}

View File

@ -1,123 +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 monasca.api.infrastructure.persistence.vertica;
import java.sql.Timestamp;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
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 monasca.api.domain.model.alarmstatehistory.AlarmStateHistory;
import monasca.api.domain.model.alarmstatehistory.AlarmStateHistoryRepo;
import monasca.api.infrastructure.persistence.DimensionQueries;
import monasca.api.infrastructure.persistence.SubAlarmDefinitionQueries;
import monasca.common.persistence.BeanMapper;
/**
* Alarm repository implementation.
*/
public class AlarmStateHistoryVerticaRepoImpl implements AlarmStateHistoryRepo {
public static final DateTimeFormatter DATETIME_FORMATTER = ISODateTimeFormat.dateTimeNoMillis()
.withZoneUTC();
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";
private final DBI mysql;
private final DBI vertica;
@Inject
public AlarmStateHistoryVerticaRepoImpl(@Named("mysql") DBI mysql, @Named("vertica") DBI vertica) {
this.mysql = mysql;
this.vertica = vertica;
}
@Override
public List<AlarmStateHistory> findById(String tenantId, String alarmId, String offset, int limit) {
// Todo. Use offset and limit for pagination.
try (Handle h = vertica.open()) {
return h
.createQuery(
"select alarm_id, old_state, new_state, reason, reason_data, time_stamp as timestamp from MonAlarms.StateHistory where tenant_id = :tenantId and alarm_id = :alarmId order by time_stamp desc")
.bind("tenantId", tenantId).bind("alarmId", alarmId)
.map(new BeanMapper<>(AlarmStateHistory.class)).list();
}
}
@Override
public List<AlarmStateHistory> find(String tenantId, Map<String, String> dimensions,
DateTime startTime, @Nullable DateTime endTime, @Nullable String offset, int limit) {
// Todo. Use offset and limit for pagination.
List<String> alarmIds = null;
// Find alarm Ids for dimensions
try (Handle h = mysql.open()) {
String sql = String.format(FIND_ALARMS_SQL, SubAlarmDefinitionQueries.buildJoinClauseFor(dimensions));
Query<Map<String, Object>> query = h.createQuery(sql).bind("tenantId", tenantId);
DimensionQueries.bindDimensionsToQuery(query, dimensions);
alarmIds = query.map(StringMapper.FIRST).list();
}
if (alarmIds == null || alarmIds.isEmpty())
return Collections.emptyList();
// Find alarm state history for alarm Ids
try (Handle h = vertica.open()) {
// Build sql
StringBuilder sbWhere = new StringBuilder();
sbWhere.append(" and alarm_id in (");
for (int i = 0; i < alarmIds.size(); i++) {
if (i > 0)
sbWhere.append(", ");
sbWhere.append('\'').append(alarmIds.get(i)).append('\'');
}
sbWhere.append(')');
if (startTime != null)
sbWhere.append(" and time_stamp >= :startTime");
if (endTime != null)
sbWhere.append(" and time_stamp <= :endTime");
String sql = String.format(FIND_BY_ALARM_DEF_SQL, sbWhere);
// Build query
Query<Map<String, Object>> query = h.createQuery(sql).bind("tenantId", tenantId);
if (startTime != null)
query.bind("startTime", new Timestamp(startTime.getMillis()));
if (endTime != null)
query.bind("endTime", new Timestamp(endTime.getMillis()));
DimensionQueries.bindDimensionsToQuery(query, dimensions);
return query.map(new BeanMapper<>(AlarmStateHistory.class)).list();
}
}
}

View File

@ -1,117 +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 monasca.api.infrastructure.persistence.vertica;
import java.nio.ByteBuffer;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.Query;
import monasca.api.domain.model.measurement.MeasurementRepo;
import monasca.api.domain.model.measurement.Measurements;
import monasca.api.infrastructure.persistence.DimensionQueries;
/**
* Vertica measurement repository implementation.
*/
public class MeasurementVerticaRepoImpl implements MeasurementRepo {
public static final DateTimeFormatter DATETIME_FORMATTER = ISODateTimeFormat.dateTimeNoMillis()
.withZoneUTC();
private static final String FIND_BY_METRIC_DEF_SQL =
"select def.name, m.definition_dimensions_id, dd.dimension_set_id, m.id, m.time_stamp, m.value "
+ "from MonMetrics.Measurements m, MonMetrics.Definitions def, MonMetrics.DefinitionDimensions dd%s "
+ "where m.definition_dimensions_id = dd.id and def.id = dd.definition_id "
+ "and def.tenant_id = :tenantId and m.time_stamp >= :startTime%s order by dd.id, m.time_stamp, m.id";
private final DBI db;
@Inject
public MeasurementVerticaRepoImpl(@Named("vertica") DBI db) {
this.db = db;
}
@Override
public List<Measurements> find(String tenantId, String name, Map<String, String> dimensions,
DateTime startTime, @Nullable DateTime endTime, @Nullable String offset, int limit,
Boolean mergeMetricsFlag) {
// Todo. Use mergeMetricsFlag.
// Todo. Use offset and limit for pagination.
try (Handle h = db.open()) {
// Build sql
StringBuilder sbWhere = new StringBuilder();
if (name != null)
sbWhere.append(" and def.name = :name");
if (endTime != null)
sbWhere.append(" and m.time_stamp <= :endTime");
String sql =
String.format(FIND_BY_METRIC_DEF_SQL, MetricQueries.buildJoinClauseFor(dimensions),
sbWhere);
// Build query
Query<Map<String, Object>> query =
h.createQuery(sql).bind("tenantId", tenantId)
.bind("startTime", new Timestamp(startTime.getMillis()));
if (name != null)
query.bind("name", name);
if (endTime != null)
query.bind("endTime", new Timestamp(endTime.getMillis()));
DimensionQueries.bindDimensionsToQuery(query, dimensions);
// Execute query
List<Map<String, Object>> rows = query.list();
// Build results
Map<ByteBuffer, Measurements> results = new LinkedHashMap<>();
for (Map<String, Object> row : rows) {
String metricName = (String) row.get("name");
byte[] defIdBytes = (byte[]) row.get("definition_dimensions_id");
byte[] dimSetIdBytes = (byte[]) row.get("dimension_set_id");
ByteBuffer defId = ByteBuffer.wrap(defIdBytes);
long measurementId = (Long) row.get("id");
String timestamp = DATETIME_FORMATTER.print(((Timestamp) row.get("time_stamp")).getTime());
double value = (double) row.get("value");
Measurements measurements = results.get(defId);
if (measurements == null) {
measurements =
new Measurements(metricName, MetricQueries.dimensionsFor(h, dimSetIdBytes),
new ArrayList<Object[]>());
results.put(defId, measurements);
}
// TODO - Really support valueMeta
measurements.addMeasurement(new Object[] {measurementId, timestamp, value, new HashMap<String, String>()});
}
return new ArrayList<Measurements>(results.values());
}
}
}

View File

@ -1,104 +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 monasca.api.infrastructure.persistence.vertica;
import java.util.ArrayList;
import java.util.Arrays;
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 monasca.api.domain.model.metric.MetricName;
import monasca.common.model.metric.MetricDefinition;
import monasca.api.domain.model.metric.MetricDefinitionRepo;
import monasca.api.infrastructure.persistence.DimensionQueries;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
/**
* Vertica metric definition repository implementation.
*/
public class MetricDefinitionVerticaRepoImpl implements MetricDefinitionRepo {
private static final String FIND_BY_METRIC_DEF_SQL =
"select dd.id, def.name, d.name as dname, d.value as dvalue "
+ "from MonMetrics.Definitions def, MonMetrics.DefinitionDimensions dd "
+ "left outer join MonMetrics.Dimensions d on d.dimension_set_id = dd.dimension_set_id%s "
+ "where def.id = dd.definition_id and def.tenant_id = :tenantId%s order by dd.id";
private final DBI db;
@Inject
public MetricDefinitionVerticaRepoImpl(@Named("vertica") DBI db) {
this.db = db;
}
@Override
public List<MetricDefinition> find(String tenantId, String name, Map<String, String> dimensions,
String offset, int limit) {
// Todo. Use offset and limit for pagination.
try (Handle h = db.open()) {
// Build sql
StringBuilder sbWhere = new StringBuilder();
if (name != null)
sbWhere.append(" and def.name = :name");
String sql =
String.format(FIND_BY_METRIC_DEF_SQL, MetricQueries.buildJoinClauseFor(dimensions),
sbWhere);
// Build query
Query<Map<String, Object>> query = h.createQuery(sql).bind("tenantId", tenantId);
if (name != null)
query.bind("name", name);
DimensionQueries.bindDimensionsToQuery(query, dimensions);
// Execute query
List<Map<String, Object>> rows = query.list();
// Build results
List<MetricDefinition> metricDefs = new ArrayList<>(rows.size());
byte[] currentId = null;
Map<String, String> dims = null;
for (Map<String, Object> row : rows) {
byte[] defId = (byte[]) row.get("id");
String metricName = (String) row.get("name");
String dName = (String) row.get("dname");
String dValue = (String) row.get("dvalue");
if (defId == null || !Arrays.equals(currentId, defId)) {
currentId = defId;
dims = new HashMap<>();
if (dName != null && dValue != null)
dims.put(dName, dValue);
metricDefs.add(new MetricDefinition(metricName, dims));
} else
dims.put(dName, dValue);
}
return metricDefs;
}
}
@Override
public List<MetricName> findNames(String tenantId, Map<String, String> dimensions, String offset, int limit) throws Exception {
throw new NotImplementedException();
}
}

View File

@ -1,47 +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 monasca.api.infrastructure.persistence.vertica;
import java.util.Map;
import org.skife.jdbi.v2.Handle;
import monasca.common.persistence.SqlQueries;
/**
* Vertica utilities for building metric queries.
*/
final class MetricQueries {
private MetricQueries() {}
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 MonMetrics.Dimensions 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 dd.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 MonMetrics.Dimensions "
+ "where" + " dimension_set_id = ?", dimensionSetId);
}
}

View File

@ -1,258 +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 monasca.api.infrastructure.persistence.vertica;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
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.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.Query;
import monasca.api.domain.model.statistic.StatisticRepo;
import monasca.api.domain.model.statistic.Statistics;
import monasca.api.infrastructure.persistence.DimensionQueries;
/**
* Vertica statistic repository implementation.
*/
public class StatisticVerticaRepoImpl implements StatisticRepo {
public static final DateTimeFormatter DATETIME_FORMATTER = ISODateTimeFormat.dateTimeNoMillis()
.withZoneUTC();
private static final String FIND_BY_METRIC_DEF_SQL =
"select dd.id, def.name, d.name as dname, d.value as dvalue "
+ "from MonMetrics.Definitions def, MonMetrics.DefinitionDimensions dd "
+ "left outer join MonMetrics.Dimensions d on d.dimension_set_id = dd.dimension_set_id%s "
+ "where def.id = dd.definition_id and def.tenant_id = :tenantId%s order by dd.id";
private final DBI db;
@Inject
public StatisticVerticaRepoImpl(@Named("vertica") DBI db) {
this.db = db;
}
@Override
public List<Statistics> find(String tenantId, String name, Map<String, String> dimensions,
DateTime startTime, DateTime endTime, List<String> statistics,
int period, String offset, int limit, Boolean mergeMetricsFlag) {
// Todo. Use mergeMetricsFlag.
// Todo. Use offset and limit for pagination.
List<Statistics> listStats = new ArrayList<>();
List<String> copyStatistics = createColumns(statistics);
try (Handle h = db.open()) {
Map<byte[], Statistics> byteMap =
findDefIds(h, tenantId, name, dimensions, startTime, endTime);
for (byte[] bufferId : byteMap.keySet()) {
Query<Map<String, Object>> query =
h.createQuery(createQuery(period, startTime, endTime, statistics))
.bind("definition_id", bufferId).bind("start_time", startTime)
.bind("end_time", endTime);
// Execute
List<Map<String, Object>> rows = query.list();
List<Object> statisticsRow = new ArrayList<Object>();
for (Map<String, Object> row : rows) {
Double sum = (Double) row.get("sum");
Double average = (Double) row.get("avg");
Double min = (Double) row.get("min");
Double max = (Double) row.get("max");
Long count = (Long) row.get("count");
Timestamp time_stamp = (Timestamp) row.get("time_interval");
if (time_stamp != null) {
statisticsRow.add(DATETIME_FORMATTER.print(time_stamp.getTime()));
}
if (average != null) {
statisticsRow.add(average);
}
if (count != null) {
statisticsRow.add(count);
}
if (max != null) {
statisticsRow.add(max);
}
if (min != null) {
statisticsRow.add(min);
}
if (sum != null) {
statisticsRow.add(sum);
}
byteMap.get(bufferId).addValues(statisticsRow);
statisticsRow = new ArrayList<>();
}
byteMap.get(bufferId).setColumns(copyStatistics);
listStats.add(byteMap.get(bufferId));
}
}
return listStats;
}
private Map<byte[], Statistics> findDefIds(Handle h, String tenantId, String name,
Map<String, String> dimensions, DateTime startTime, DateTime endTime) {
List<byte[]> bytes = new ArrayList<>();
// Build query
StringBuilder sbWhere = new StringBuilder();
if (name != null)
sbWhere.append(" and def.name = :name");
String sql =
String
.format(FIND_BY_METRIC_DEF_SQL, MetricQueries.buildJoinClauseFor(dimensions), sbWhere);
Query<Map<String, Object>> query =
h.createQuery(sql).bind("tenantId", tenantId).bind("startTime", startTime);
if (name != null) {
query.bind("name", name);
}
if (endTime != null) {
query.bind("endTime", new Timestamp(endTime.getMillis()));
}
DimensionQueries.bindDimensionsToQuery(query, dimensions);
// Execute
List<Map<String, Object>> rows = query.list();
Map<byte[], Statistics> byteIdMap = new HashMap<>();
// Build results
byte[] currentId = null;
Map<String, String> dims = null;
for (Map<String, Object> row : rows) {
byte[] defId = (byte[]) row.get("id");
String defName = (String) row.get("name");
String demName = (String) row.get("dname");
String demValue = (String) row.get("dvalue");
if (defId == null || !Arrays.equals(currentId, defId)) {
currentId = defId;
dims = new HashMap<>();
dims.put(demName, demValue);
Statistics statistics = new Statistics();
statistics.setName(defName);
statistics.setDimensions(dims);
byteIdMap.put(currentId, statistics);
} else
dims.put(demName, demValue);
}
bytes.add(currentId);
return byteIdMap;
}
List<String> createColumns(List<String> list) {
List<String> copy = new ArrayList<>();
for (String string : list) {
copy.add(string);
}
Collections.sort(copy);
copy.add(0, "timestamp");
return copy;
}
private String createQuery(int period, DateTime startTime, DateTime endTime,
List<String> statistics) {
StringBuilder builder = new StringBuilder();
builder.append("SELECT " + getColumns(statistics));
if (period >= 1) {
builder.append(",MIN(time_stamp) as time_interval ");
builder.append(" FROM (Select FLOOR((EXTRACT('epoch' from time_stamp) - ");
builder.append(createOffset(period, startTime, endTime));
builder.append(" AS time_slice, time_stamp, value ");
}
builder.append(" FROM MonMetrics.Measurements ");
builder.append("WHERE definition_dimensions_id = :definition_id ");
builder.append(createWhereClause(startTime, endTime));
if (period >= 1) {
builder.append(") as TimeSlices group by time_slice order by time_slice");
}
return builder.toString();
}
private String createWhereClause(DateTime startTime, DateTime endTime) {
String clause = "";
if (startTime != null && endTime != null) {
clause = "AND time_stamp >= :start_time AND time_stamp <= :end_time ";
} else if (startTime != null) {
clause = "AND time_stamp >= :start_time ";
}
return clause;
}
private String createOffset(int period, DateTime startTime, DateTime endTime) {
StringBuilder offset = new StringBuilder();
offset
.append("(select mod((select extract('epoch' from time_stamp) from MonMetrics.Measurements ");
offset.append("WHERE definition_dimensions_id = :definition_id ");
offset.append(createWhereClause(startTime, endTime));
offset.append("order by time_stamp limit 1");
offset.append("),");
offset.append(period + ")))/" + period + ")");
return offset.toString();
}
private String getColumns(List<String> statistics) {
StringBuilder buildColumns = new StringBuilder();
int size = statistics.size();
int count = 0;
for (String statistic : statistics) {
if (statistic.equals("average")) {
buildColumns.append("avg(value) as average ");
} else {
buildColumns.append(statistic + "(value) as " + statistic + " ");
}
if (size - 1 > count) {
buildColumns.append(",");
}
count++;
}
return buildColumns.toString();
}
}

View File

@ -1,93 +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 monasca.api.infrastructure.servlet;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* Mocks authentication by converting X-Auth-Token headers to X-Tenant-Ids.
*/
public class MockAuthenticationFilter implements Filter {
private static final String X_AUTH_TOKEN_HEADER = "X-Auth-Token";
@Override
public void destroy() {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest req = (HttpServletRequest) request;
HttpServletRequestWrapper wrapper = requestWrapperFor(req);
chain.doFilter(wrapper, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
/**
* Returns an HttpServletRequestWrapper that serves tenant id headers from request attributes.
*/
private HttpServletRequestWrapper requestWrapperFor(final HttpServletRequest request) {
return new HttpServletRequestWrapper(request) {
@Override
public Object getAttribute(String name) {
if (name.equalsIgnoreCase(PostAuthenticationFilter.X_TENANT_ID_HEADER)) {
String tenantId = request.getHeader(PostAuthenticationFilter.X_TENANT_ID_HEADER);
return tenantId == null ? request.getHeader(X_AUTH_TOKEN_HEADER) : tenantId;
}
if (name.equalsIgnoreCase(PostAuthenticationFilter.X_IDENTITY_STATUS_ATTRIBUTE))
return PostAuthenticationFilter.CONFIRMED_STATUS;
if (name.equalsIgnoreCase(PostAuthenticationFilter.X_ROLES_ATTRIBUTE))
return "user";
return super.getAttribute(name);
}
@Override
public String getHeader(String name) {
if (name.equalsIgnoreCase(PostAuthenticationFilter.X_TENANT_ID_HEADER))
return request.getHeader(X_AUTH_TOKEN_HEADER);
return super.getHeader(name);
}
@Override
public Enumeration<String> getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
names.add(PostAuthenticationFilter.X_TENANT_ID_HEADER);
return Collections.enumeration(names);
}
@Override
public Enumeration<String> getHeaders(String name) {
if (name.equalsIgnoreCase(PostAuthenticationFilter.X_TENANT_ID_HEADER)) {
String authToken = request.getHeader(X_AUTH_TOKEN_HEADER);
return authToken == null ? Collections.<String>emptyEnumeration() : Collections
.enumeration(Collections.singleton(authToken));
}
return super.getHeaders(name);
}
};
}
}

View File

@ -1,192 +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 monasca.api.infrastructure.servlet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import javax.annotation.Nullable;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType;
import monasca.api.infrastructure.servlet.PreAuthenticationFilter.ErrorCapturingServletResponseWrapper;
/**
* Authenticates requests using header information from the CsMiddleware. Provides the X-TENANT-ID
* servlet attribute as a request header. Intended to be added to a servlet filter chain after the
* CsMiddleware TokenAuth filter.
*/
public class PostAuthenticationFilter implements Filter {
static final String CONFIRMED_STATUS = "CONFIRMED";
static final String X_ROLES_ATTRIBUTE = "X-ROLES";
static final String X_MONASCA_AGENT = "X-MONASCA_AGENT";
static final String X_IDENTITY_STATUS_ATTRIBUTE = "X-IDENTITY-STATUS";
private static final String X_TENANT_ID_ATTRIBUTE = "X-PROJECT-ID";
static final String X_TENANT_ID_HEADER = "X-Tenant-Id";
static final String X_ROLES_HEADER = "X-Roles";
private final List<String> defaultAuthorizedRoles = new ArrayList<String>();
private final List<String> agentAuthorizedRoles = new ArrayList<String>();
public PostAuthenticationFilter(List<String> defaultAuthorizedRoles,
List<String> agentAuthorizedRoles) {
for (String defaultRole : defaultAuthorizedRoles) {
this.defaultAuthorizedRoles.add(defaultRole.toLowerCase());
}
for (String agentRole : agentAuthorizedRoles) {
this.agentAuthorizedRoles.add(agentRole.toLowerCase());
}
}
@Override
public void destroy() {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
final HttpServletRequest req = (HttpServletRequest) request;
ErrorCapturingServletResponseWrapper res = (ErrorCapturingServletResponseWrapper) response;
String tenantIdStr = null;
try {
// According to CORS spec OPTIONS method does not pass auth info
if (req.getMethod().equals("OPTIONS")) {
chain.doFilter(request, response);
return;
}
Object tenantId = request.getAttribute(X_TENANT_ID_ATTRIBUTE);
if (tenantId == null) {
sendAuthError(res, null, null, null);
return;
}
tenantIdStr = tenantId.toString();
boolean authenticated = isAuthenticated(req);
boolean authorized = isAuthorized(req);
if (authenticated && authorized) {
HttpServletRequestWrapper wrapper = requestWrapperFor(req);
chain.doFilter(wrapper, response);
return;
}
if (authorized)
sendAuthError(res, tenantIdStr, null, null);
else
sendAuthError(res, tenantIdStr, "Tenant is missing a required role to access this service",
null);
} catch (Exception e) {
try {
sendAuthError(res, tenantIdStr, null, e);
} catch (IOException ignore) {
}
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
/**
* @return true if the request is authenticated else false
*/
private boolean isAuthenticated(HttpServletRequest request) {
Object identityStatus = request.getAttribute(X_IDENTITY_STATUS_ATTRIBUTE);
return identityStatus != null && CONFIRMED_STATUS.equalsIgnoreCase(identityStatus.toString());
}
/**
* @return true if the request is authorized else false
*/
private boolean isAuthorized(HttpServletRequest request) {
Object rolesFromKeystone = request.getAttribute(X_ROLES_ATTRIBUTE);
if (rolesFromKeystone == null)
return false;
boolean agentUser = false;
for (String role : rolesFromKeystone.toString().split(",")) {
String lowerCaseRole = role.toLowerCase();
if ((defaultAuthorizedRoles != null) && defaultAuthorizedRoles.contains(lowerCaseRole)) {
return true;
}
if ((agentAuthorizedRoles != null) && agentAuthorizedRoles.contains(lowerCaseRole)) {
agentUser = true;
}
}
if (agentUser) {
request.setAttribute(X_MONASCA_AGENT, true);
return true;
}
return false;
}
/**
* Returns an HttpServletRequestWrapper that serves tenant id headers from request attributes.
*/
private HttpServletRequestWrapper requestWrapperFor(final HttpServletRequest request) {
return new HttpServletRequestWrapper(request) {
@Override
public String getHeader(String name) {
if (name.equalsIgnoreCase(X_TENANT_ID_HEADER))
return request.getAttribute(X_TENANT_ID_ATTRIBUTE).toString();
else if (name.equalsIgnoreCase(X_ROLES_HEADER))
return request.getAttribute(X_ROLES_ATTRIBUTE).toString();
return super.getHeader(name);
}
@Override
public Enumeration<String> getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
names.add(X_TENANT_ID_HEADER);
names.add(X_ROLES_HEADER);
return Collections.enumeration(names);
}
@Override
public Enumeration<String> getHeaders(String name) {
if (name.equalsIgnoreCase(X_TENANT_ID_HEADER))
return Collections.enumeration(Collections.singleton(request.getAttribute(
X_TENANT_ID_ATTRIBUTE).toString()));
else if (name.equalsIgnoreCase(X_ROLES_HEADER))
return Collections.enumeration(Collections.singleton(request.getAttribute(
X_ROLES_ATTRIBUTE).toString()));
return super.getHeaders(name);
}
};
}
private void sendAuthError(ErrorCapturingServletResponseWrapper response,
@Nullable String tenantId, @Nullable String message, @Nullable Exception exception)
throws IOException {
response.setContentType(MediaType.APPLICATION_JSON);
if (message == null)
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
tenantId == null ? "Failed to authenticate request"
: "Failed to authenticate request for " + tenantId, exception);
else
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, String.format(message, tenantId));
}
}

View File

@ -1,134 +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 monasca.api.infrastructure.servlet;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.ws.rs.core.MediaType;
import org.eclipse.jetty.server.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import monasca.api.resource.exception.Exceptions;
import monasca.api.resource.exception.Exceptions.FaultType;
/**
* Authenticates requests using header information from the CsMiddleware. Provides the X-TENANT-ID
* servlet attribute as a request header. Intended to be added to a servlet filter chain after the
* CsMiddleware TokenAuth filter.
*/
public class PreAuthenticationFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(PreAuthenticationFilter.class);
static class ErrorCapturingServletResponseWrapper extends HttpServletResponseWrapper {
private int statusCode;
private String errorMessage;
private Exception exception;
public ErrorCapturingServletResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public void sendError(int statusCode) throws IOException {
this.statusCode = statusCode;
}
@Override
public void sendError(int statusCode, String msg) throws IOException {
this.statusCode = statusCode;
errorMessage = msg;
}
void sendError(int statusCode, String msg, Exception exception) throws IOException {
sendError(statusCode, msg);
this.exception = exception;
}
}
@Override
public void destroy() {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletResponse res = (HttpServletResponse) response;
ErrorCapturingServletResponseWrapper responseWrapper =
new ErrorCapturingServletResponseWrapper(res);
boolean caughtException = false;
ServletOutputStream out = null;
try {
out = res.getOutputStream();
chain.doFilter(request, responseWrapper);
if (responseWrapper.statusCode != 401 && responseWrapper.statusCode != 500)
return;
} catch (Exception e) {
LOG.error("Error while executing pre authentication filter", e);
caughtException = true;
}
try {
res.setContentType(MediaType.APPLICATION_JSON);
if (caughtException) {
res.setStatus(Response.SC_INTERNAL_SERVER_ERROR);
}
else {
res.setStatus(responseWrapper.statusCode);
FaultType faultType;
if (responseWrapper.statusCode == 500) {
faultType = FaultType.SERVER_ERROR;
}
else {
faultType = FaultType.UNAUTHORIZED;
}
String output = Exceptions.buildLoggedErrorMessage(faultType, responseWrapper.errorMessage,
null, responseWrapper.exception);
out.print(output);
}
} catch (IllegalArgumentException e) {
// CSMiddleware is throwing this error for invalid tokens.
// This problem appears to be fixed in other versions, but they are not approved yet.
try {
String output =
Exceptions.buildLoggedErrorMessage(FaultType.UNAUTHORIZED, "invalid authToken", null,
responseWrapper.exception);
out.print(output);
} catch (Exception x) {
LOG.error("Error while writing failed authentication HTTP response", x);
}
} catch (Exception e) {
LOG.error("Error while writing failed authentication HTTP response", e);
} finally {
if (out != null)
try {
out.close();
} catch (IOException ignore) {
}
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
}

View File

@ -1,70 +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 monasca.api.infrastructure.servlet;
import monasca.api.resource.exception.Exceptions;
import monasca.common.middleware.AuthConstants;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
import static monasca.api.infrastructure.servlet.PostAuthenticationFilter.X_MONASCA_AGENT;
public class RoleAuthorizationFilter implements ContainerRequestFilter {
private static final Logger logger = LoggerFactory.getLogger
(ContainerRequestFilter.class);
@Context
private HttpServletRequest httpServletRequest;
private static final String[] VALID_MONASCA_AGENT_POST_PATHS = new String[] { "/v2.0/metrics" };
private static final String[] VALID_MONASCA_AGENT_GET_PATHS = new String[] { "/", "/v2.0" };
@Override
public ContainerRequest filter(ContainerRequest containerRequest) {
String method = containerRequest.getMethod();
Object isAgent = httpServletRequest.getAttribute(X_MONASCA_AGENT);
String pathInfo = httpServletRequest.getPathInfo();
// X_MONASCA_AGENT is only set if the only valid role for this user is an agent role
if (isAgent != null) {
if (!(method.equals("POST") && validPath(pathInfo, VALID_MONASCA_AGENT_POST_PATHS)) &&
!(method.equals("GET") && validPath(pathInfo, VALID_MONASCA_AGENT_GET_PATHS))) {
logger.warn("User {} is missing a required role to {} on {}",
httpServletRequest.getAttribute(AuthConstants.AUTH_USER_NAME),
method, pathInfo);
throw Exceptions.badRequest("User is missing a required role to perform this request");
}
}
return containerRequest;
}
private boolean validPath(String pathInfo, String[] paths) {
// Make the comparison easier by getting rid of trailing slashes
while (!pathInfo.isEmpty() && !"/".equals(pathInfo) && pathInfo.endsWith("/")) {
pathInfo = pathInfo.substring(0, pathInfo.length() - 1);
}
for (final String validPath : paths) {
if (validPath.equals(pathInfo)) {
return true;
}
}
return false;
}
}

View File

@ -1,171 +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 monasca.api.resource;
import com.google.common.base.Strings;
import com.codahale.metrics.annotation.Timed;
import com.fasterxml.jackson.databind.JsonMappingException;
import org.hibernate.validator.constraints.NotEmpty;
import java.io.UnsupportedEncodingException;
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 monasca.api.app.AlarmDefinitionService;
import monasca.api.app.command.CreateAlarmDefinitionCommand;
import monasca.api.app.command.UpdateAlarmDefinitionCommand;
import monasca.api.app.validation.AlarmValidation;
import monasca.api.app.validation.Validation;
import monasca.api.domain.model.alarmdefinition.AlarmDefinition;
import monasca.api.domain.model.alarmdefinition.AlarmDefinitionRepo;
import monasca.api.infrastructure.persistence.PersistUtils;
import monasca.api.resource.annotation.PATCH;
import monasca.common.model.alarm.AlarmExpression;
/**
* Alarm definition resource implementation.
*/
@Path(AlarmDefinitionResource.ALARM_DEFINITIONS_PATH)
public class AlarmDefinitionResource {
private final AlarmDefinitionService service;
private final AlarmDefinitionRepo repo;
private final PersistUtils persistUtils;
public final static String ALARM_DEFINITIONS = "alarm-definitions";
public final static String ALARM_DEFINITIONS_PATH = "/v2.0/" + ALARM_DEFINITIONS;
@Inject
public AlarmDefinitionResource(AlarmDefinitionService service,
AlarmDefinitionRepo repo,
PersistUtils persistUtils) {
this.service = service;
this.repo = repo;
this.persistUtils = persistUtils;
}
@POST
@Timed
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
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)
public Object list(@Context UriInfo uriInfo,
@HeaderParam("X-Tenant-Id") String tenantId, @QueryParam("name") String name,
@QueryParam("dimensions") String dimensionsStr,
@QueryParam("offset") String offset,
@QueryParam("limit") String limit) throws UnsupportedEncodingException {
Map<String, String> dimensions =
Strings.isNullOrEmpty(dimensionsStr) ? null : Validation
.parseAndValidateDimensions(dimensionsStr);
return Links.paginate(this.persistUtils.getLimit(limit),
Links.hydrate(repo.find(tenantId, name, dimensions, offset,
this.persistUtils.getLimit(limit)), uriInfo),
uriInfo);
}
@GET
@Timed
@Path("/{alarm_definition_id}")
@Produces(MediaType.APPLICATION_JSON)
public AlarmDefinition get(
@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)
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(MediaType.APPLICATION_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");
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, enabled, alarmActions, okActions,
undeterminedActions), uriInfo, true);
}
@DELETE
@Timed
@Path("/{alarm_definition_id}")
public void delete(@HeaderParam("X-Tenant-Id") String tenantId,
@PathParam("alarm_definition_id") String alarmDefinitionId) {
service.delete(tenantId, alarmDefinitionId);
}
}

View File

@ -1,189 +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 monasca.api.resource;
import com.google.common.base.Strings;
import com.codahale.metrics.annotation.Timed;
import com.fasterxml.jackson.databind.JsonMappingException;
import org.hibernate.validator.constraints.NotEmpty;
import org.joda.time.DateTime;
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.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.UriInfo;
import monasca.api.app.AlarmService;
import monasca.api.app.command.UpdateAlarmCommand;
import monasca.api.app.validation.Validation;
import monasca.api.domain.model.alarm.Alarm;
import monasca.api.domain.model.alarm.AlarmRepo;
import monasca.api.domain.model.alarmstatehistory.AlarmStateHistoryRepo;
import monasca.api.infrastructure.persistence.PersistUtils;
import monasca.api.resource.annotation.PATCH;
import monasca.common.model.alarm.AlarmState;
/**
* Alarm resource implementation.
*/
@Path("/v2.0/alarms")
public class AlarmResource {
private final AlarmService service;
private final AlarmRepo repo;
private final PersistUtils persistUtils;
private final AlarmStateHistoryRepo stateHistoryRepo;
@Inject
public AlarmResource(AlarmService service, AlarmRepo repo,
AlarmStateHistoryRepo stateHistoryRepo,
PersistUtils persistUtils) {
this.service = service;
this.repo = repo;
this.stateHistoryRepo = stateHistoryRepo;
this.persistUtils = persistUtils;
}
@DELETE
@Timed
@Path("/{alarm_id}")
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)
public Alarm get(
@Context UriInfo uriInfo,
@HeaderParam("X-Tenant-Id") String tenantId, @PathParam("alarm_id") String alarm_id) {
return fixAlarmLinks(uriInfo, repo.findById(tenantId, alarm_id));
}
private Alarm fixAlarmLinks(UriInfo uriInfo, Alarm alarm) {
Links.hydrate(alarm.getAlarmDefinition(), uriInfo,
AlarmDefinitionResource.ALARM_DEFINITIONS_PATH);
return Links.hydrate(alarm, uriInfo, true);
}
@GET
@Timed
@Path("/{alarm_id}/state-history")
@Produces(MediaType.APPLICATION_JSON)
public Object getStateHistory(@Context UriInfo uriInfo,
@HeaderParam("X-Tenant-Id") String tenantId, @PathParam("alarm_id") String alarmId,
@QueryParam("offset") String offset,
@QueryParam("limit") String limit)
throws Exception {
return Links.paginate(this.persistUtils.getLimit(limit),
stateHistoryRepo.findById(tenantId, alarmId, offset,
this.persistUtils.getLimit(limit)), uriInfo);
}
@GET
@Timed
@Path("/state-history")
@Produces(MediaType.APPLICATION_JSON)
public Object listStateHistory(
@Context UriInfo uriInfo,
@HeaderParam("X-Tenant-Id") String tenantId, @QueryParam("dimensions") String dimensionsStr,
@QueryParam("start_time") String startTimeStr, @QueryParam("end_time") String endTimeStr,
@QueryParam("offset") String offset,
@QueryParam("limit") String limit)
throws Exception {
// Validate query parameters
DateTime startTime = Validation.parseAndValidateDate(startTimeStr, "start_time", false);
DateTime endTime = Validation.parseAndValidateDate(endTimeStr, "end_time", false);
if (startTime != null)
Validation.validateTimes(startTime, endTime);
Map<String, String> dimensions =
Strings.isNullOrEmpty(dimensionsStr) ? null : Validation
.parseAndValidateDimensions(dimensionsStr);
return Links.paginate(this.persistUtils.getLimit(limit),
stateHistoryRepo.find(tenantId, dimensions, startTime,
endTime, offset, this.persistUtils.getLimit(limit)), uriInfo);
}
@GET
@Timed
@Produces(MediaType.APPLICATION_JSON)
public Object list(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
@QueryParam("alarm_definition_id") String alarmDefId,
@QueryParam("metric_name") String metricName,
@QueryParam("metric_dimensions") String metricDimensionsStr,
@QueryParam("state") AlarmState state,
@QueryParam("state_updated_start_time") String stateUpdatedStartStr,
@QueryParam("offset") String offset,
@QueryParam("limit") String limit)
throws Exception {
Map<String, String> metricDimensions =
Strings.isNullOrEmpty(metricDimensionsStr) ? null : Validation
.parseAndValidateNameAndDimensions(metricName, metricDimensionsStr, false);
DateTime stateUpdatedStart =
Validation.parseAndValidateDate(stateUpdatedStartStr,
"state_updated_start_time", false);
final List<Alarm> alarms = repo.find(tenantId, alarmDefId, metricName, metricDimensions, state, stateUpdatedStart,
offset, this.persistUtils.getLimit(limit), true);
for (final Alarm alarm : alarms) {
Links.hydrate(alarm.getAlarmDefinition(), uriInfo, AlarmDefinitionResource.ALARM_DEFINITIONS_PATH);
}
return Links.paginate(this.persistUtils.getLimit(limit), Links.hydrate(alarms, uriInfo), uriInfo);
}
@PATCH
@Timed
@Path("/{alarm_id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
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 fixAlarmLinks(uriInfo, service.patch(tenantId, alarmId, state));
}
@PUT
@Timed
@Path("/{alarm_id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Alarm update(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
@PathParam("alarm_id") String alarmId, @Valid UpdateAlarmCommand command) {
return fixAlarmLinks(uriInfo, service.update(tenantId, alarmId, command));
}
}

View File

@ -1,351 +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 monasca.api.resource;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.core.UriInfo;
import com.google.common.base.Preconditions;
import monasca.api.ApiConfig;
import monasca.api.domain.model.common.Paged;
import monasca.api.domain.model.measurement.Measurements;
import monasca.api.domain.model.statistic.Statistics;
import monasca.common.model.domain.common.AbstractEntity;
import monasca.api.domain.model.common.Link;
import monasca.api.domain.model.common.Linked;
import monasca.common.util.Injector;
/**
* Utilities for working with links.
*/
public final class Links {
static boolean accessedViaHttps;
static {
ApiConfig config = Injector.getInstance(ApiConfig.class);
if (config != null && config.accessedViaHttps != null)
accessedViaHttps = config.accessedViaHttps;
}
/**
* Hydrates the {@code resources} with links for the {@code uriInfo}.
*
* @throws NullPointerException if {@code resource} is null
*/
public static <T extends AbstractEntity & Linked> List<T> hydrate(List<T> resources,
UriInfo uriInfo, String... children) {
Preconditions.checkNotNull(resources, "resources");
// Safe since this path should not be specific to a resource
String absolutePath = prefixForHttps(uriInfo.getAbsolutePath().toString());
for (T resource : resources)
hydrate(resource, absolutePath, false, children);
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}.
*
* @param resource to obtain id from
* @param uriInfo to obtain base path from
* @param resourcePath path to type of resource
* @throws NullPointerException if {@code resource} is null
*/
public static <T extends AbstractEntity & Linked> T hydrate(T resource, UriInfo uriInfo, String resourcePath) {
return hydrate(resource, concatPaths(uriInfo.getBaseUri().toString(), resourcePath) + "/", false);
}
private static String concatPaths(final String first, final String second) {
// Check if this would cause two slashes in a row or a slash at the start
if ((first.isEmpty() || first.endsWith("/")) && !second.isEmpty() && second.startsWith("/")) {
return first + second.substring(1);
}
else {
return first + second;
}
}
/**
* Hydrates the {@code resource} with links for the {@code uriInfo}.
*
* @param resource to obtain id from
* @param uriInfo to obtain path from
* @param uriInfoForSpecificResource whether the uriInfo is for a specific resource
* @param children child link elements to create
* @throws NullPointerException if {@code resource} is null
*/
public static <T extends AbstractEntity & Linked> T hydrate(T resource, UriInfo uriInfo,
boolean uriInfoForSpecificResource, String... children) {
return hydrate(resource, prefixForHttps(uriInfo.getAbsolutePath().toString()),
uriInfoForSpecificResource, children);
}
/**
* Returns a string that is prefixed for prefixForHttp if https is being used.
*/
static String prefixForHttps(String path) {
if (accessedViaHttps && !path.toLowerCase().startsWith("https"))
path = "https" + path.substring(path.indexOf("://"));
return path;
}
/**
* Hydrates the {@code resource} with links for the {@code path}.
*
* @throws NullPointerException if {@code resource} is null
*/
private static <T extends AbstractEntity & Linked> T hydrate(T resource, String path,
boolean pathForSpecificResource, String... children) {
Preconditions.checkNotNull(resource, "resource");
List<Link> links = new ArrayList<>(children.length + 1);
if (!pathForSpecificResource) {
boolean pathEndsInSlash = path.length() > 0 && path.charAt(path.length() - 1) == '/';
if (!pathEndsInSlash)
path += "/";
path += resource.getId();
}
links.add(new Link("self", path));
for (String child : children)
links.add(new Link(child, path + "/" + child));
resource.setLinks(links);
return resource;
}
/**
* This method handles the case that the elements list size is one greater than the
* limit. The next link will be created automatically.
*
* This method also handles the case that the element size is the limit. The next
* link will not be created.
*
* The convention is for methods that query the DB to request limit + 1 elements.
*
* Only limit number of elements will be returned.
*
* @param limit
* @param elements
* @param uriInfo
* @return
*/
public static Object paginate(int limit, List<? extends AbstractEntity> elements, UriInfo uriInfo)
throws UnsupportedEncodingException {
// Check for paging turned off. Happens if maxQueryLimit is not set or is set to zero.
if (limit == 0) {
Paged paged = new Paged();
paged.elements = elements != null ? elements : new ArrayList<>();
return paged;
}
Paged paged = new Paged();
paged.links.add(getSelfLink(uriInfo));
if (elements != null) {
if (elements.size() > limit) {
String offset = elements.get(limit - 1).getId();
paged.links.add(getNextLink(offset, uriInfo));
// Truncate the list. Normally this will just truncate one extra element.
elements = elements.subList(0, limit);
}
paged.elements = elements;
} else {
paged.elements = new ArrayList();
}
return paged;
}
public static Object paginateMeasurements(int limit, List<Measurements> elements, UriInfo uriInfo)
throws UnsupportedEncodingException {
// Check for paging turned off. Happens if maxQueryLimit is not set or is set to zero.
if (limit == 0) {
Paged paged = new Paged();
paged.elements = elements != null ? elements : new ArrayList<>();
return paged;
}
Paged paged = new Paged();
paged.links.add(getSelfLink(uriInfo));
if (elements != null && !elements.isEmpty()) {
Measurements m = elements.get(0);
if (m != null) {
List<Object[]> l = m.getMeasurements();
if (l.size() > limit) {
String offset = (String) l.get(limit - 1)[0];
m.setId(offset);
paged.links.add(getNextLink(offset, uriInfo));
// Truncate the list. Normally this will just truncate one extra element.
l = l.subList(0, limit);
m.setMeasurements(l);
}
// Check if there are any elements.
if (l.size() > 0) {
// Set the id to the last date in the list.
m.setId((String) l.get(l.size() - 1)[0]);
}
paged.elements = elements;
} else {
paged.elements = new ArrayList<>();
}
} else {
paged.elements = new ArrayList();
}
return paged;
}
public static Object paginateStatistics(int limit, List<Statistics> elements, UriInfo uriInfo)
throws UnsupportedEncodingException {
// Check for paging turned off. Happens if maxQueryLimit is not set or is set to zero.
if (limit == 0) {
Paged paged = new Paged();
paged.elements = elements != null ? elements : new ArrayList<>();
return paged;
}
Paged paged = new Paged();
paged.links.add(getSelfLink(uriInfo));
if (elements != null && !elements.isEmpty()) {
Statistics s = elements.get(0);
if (s != null) {
List<List<Object>> l = s.getStatistics();
if (l.size() > limit) {
String offset = (String) l.get(limit - 1).get(0);
s.setId(offset);
paged.links.add(getNextLink(offset, uriInfo));
// Truncate the list. Normally this will just truncate one extra element.
l = l.subList(0, limit);
s.setStatistics(l);
}
// Check if there are any elements.
if (l.size() > 0) {
// Set the id to the last date in the list.
s.setId((String) l.get(l.size() - 1).get(0));
}
paged.elements = elements;
} else {
paged.elements = new ArrayList<>();
}
} else {
paged.elements = new ArrayList();
}
return paged;
}
private static Link getSelfLink(UriInfo uriInfo) {
Link selfLink = new Link();
selfLink.rel = "self";
selfLink.href = uriInfo.getRequestUri().toString();
return selfLink;
}
private static Link getNextLink(String offset, UriInfo uriInfo)
throws UnsupportedEncodingException {
Link nextLink = new Link();
nextLink.rel = "next";
// Create a new URL with the new offset.
nextLink.href = uriInfo.getAbsolutePath().toString()
+ "?offset=" + URLEncoder.encode(offset, "UTF-8");
// Add the query parms back to the URL without the original offset.
for (String parmKey : uriInfo.getQueryParameters().keySet()) {
if (!parmKey.equalsIgnoreCase("offset")) {
List<String> parmValList = uriInfo.getQueryParameters().get(parmKey);
for (String parmVal : parmValList) {
nextLink.href +=
"&" + URLEncoder.encode(parmKey, "UTF-8") + "=" + URLEncoder.encode(parmVal, "UTF-8");
}
}
}
return nextLink;
}
}

View File

@ -1,82 +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 monasca.api.resource;
import com.google.common.base.Strings;
import com.codahale.metrics.annotation.Timed;
import org.joda.time.DateTime;
import java.util.Map;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
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.UriInfo;
import monasca.api.app.validation.Validation;
import monasca.api.domain.model.measurement.MeasurementRepo;
import monasca.api.infrastructure.persistence.PersistUtils;
/**
* Measurement resource implementation.
*/
@Path("/v2.0/metrics/measurements")
public class MeasurementResource {
private final MeasurementRepo repo;
private final PersistUtils persistUtils;
@Inject
public MeasurementResource(MeasurementRepo repo, PersistUtils persistUtils) {
this.repo = repo;
this.persistUtils = persistUtils;
}
@GET
@Timed
@Produces(MediaType.APPLICATION_JSON)
public Object get(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
@QueryParam("name") String name, @QueryParam("dimensions") String dimensionsStr,
@QueryParam("start_time") String startTimeStr,
@QueryParam("end_time") String endTimeStr,
@QueryParam("offset") String offset,
@QueryParam("limit") String limit,
@QueryParam("merge_metrics") Boolean mergeMetricsFlag)
throws Exception {
// Validate query parameters
DateTime startTime = Validation.parseAndValidateDate(startTimeStr, "start_time", true);
DateTime endTime = Validation.parseAndValidateDate(endTimeStr, "end_time", false);
Validation.validateTimes(startTime, endTime);
Map<String, String>
dimensions =
Strings.isNullOrEmpty(dimensionsStr) ? null : Validation
.parseAndValidateNameAndDimensions(name, dimensionsStr, true);
return Links.paginateMeasurements(this.persistUtils.getLimit(limit),
repo.find(tenantId, name, dimensions, startTime, endTime,
offset, this.persistUtils.getLimit(limit),
mergeMetricsFlag),
uriInfo);
}
}

View File

@ -1,136 +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 monasca.api.resource;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.codahale.metrics.annotation.Timed;
import java.util.ArrayList;
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.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
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.UriInfo;
import monasca.api.app.MetricService;
import monasca.api.app.command.CreateMetricCommand;
import monasca.api.app.validation.Validation;
import monasca.api.domain.model.metric.MetricDefinitionRepo;
import monasca.api.infrastructure.persistence.PersistUtils;
import monasca.api.resource.exception.Exceptions;
import monasca.common.model.Services;
import monasca.common.model.metric.Metric;
/**
* Metric resource implementation.
*/
@Path("/v2.0/metrics")
public class MetricResource {
private static final String MONITORING_DELEGATE_ROLE = "monitoring-delegate";
private static final Splitter COMMA_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults();
private final MetricService service;
private final MetricDefinitionRepo metricRepo;
private final PersistUtils persistUtils;
@Inject
public MetricResource(MetricService service, MetricDefinitionRepo metricRepo,
PersistUtils persistUtils) {
this.service = service;
this.metricRepo = metricRepo;
this.persistUtils = persistUtils;
}
@POST
@Timed
@Consumes(MediaType.APPLICATION_JSON)
public void create(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
@HeaderParam("X-Roles") String roles,
@QueryParam("tenant_id") String crossTenantId,
@Valid CreateMetricCommand[] commands) {
boolean
isDelegate =
!Strings.isNullOrEmpty(roles) && COMMA_SPLITTER.splitToList(roles)
.contains(MONITORING_DELEGATE_ROLE);
List<Metric> metrics = new ArrayList<>(commands.length);
for (CreateMetricCommand command : commands) {
if (!isDelegate) {
if (command.dimensions != null) {
String service = command.dimensions.get(Services.SERVICE_DIMENSION);
if (service != null && Services.isReserved(service)) {
throw Exceptions
.forbidden("Project %s cannot POST metrics for the hpcs service", tenantId);
}
}
if (!Strings.isNullOrEmpty(crossTenantId) && !crossTenantId.equals(tenantId)) {
throw Exceptions.forbidden("Project %s cannot POST cross tenant metrics", tenantId);
}
}
command.validate();
metrics.add(command.toMetric());
}
service.create(metrics, tenantId, crossTenantId);
}
@GET
@Timed
@Produces(MediaType.APPLICATION_JSON)
public Object getMetrics(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
@QueryParam("name") String name,
@QueryParam("dimensions") String dimensionsStr,
@QueryParam("offset") String offset,
@QueryParam("limit") String limit)
throws Exception {
Map<String, String>
dimensions =
Strings.isNullOrEmpty(dimensionsStr) ? null : Validation
.parseAndValidateNameAndDimensions(name, dimensionsStr, false);
return Links.paginate(this.persistUtils.getLimit(limit),
metricRepo.find(tenantId, name, dimensions, offset, this.persistUtils.getLimit(limit)), uriInfo);
}
@GET
@Path("/names")
@Timed
@Produces(MediaType.APPLICATION_JSON)
public Object getMetricNames(@Context UriInfo uriInfo,
@HeaderParam("X-Tenant-Id") String tenantId,
@QueryParam("dimensions") String dimensionsStr,
@QueryParam("offset") String offset,
@QueryParam("limit") String limit) throws Exception {
Map<String, String>
dimensions =
Strings.isNullOrEmpty(dimensionsStr) ? null : Validation
.parseAndValidateDimensions(dimensionsStr);
return Links.paginate(this.persistUtils.getLimit(limit),
metricRepo.findNames(tenantId, dimensions, offset, this.persistUtils.getLimit(limit)), uriInfo);
}
}

View File

@ -1,119 +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 monasca.api.resource;
import com.codahale.metrics.annotation.Timed;
import java.io.UnsupportedEncodingException;
import java.net.URI;
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 monasca.api.app.command.CreateNotificationMethodCommand;
import monasca.api.domain.model.notificationmethod.NotificationMethod;
import monasca.api.domain.model.notificationmethod.NotificationMethodRepo;
import monasca.api.infrastructure.persistence.PersistUtils;
/**
* Notification Method resource implementation.
*/
@Path("/v2.0/notification-methods")
public class NotificationMethodResource {
private final NotificationMethodRepo repo;
private final PersistUtils persistUtils;
@Inject
public NotificationMethodResource(NotificationMethodRepo repo, PersistUtils persistUtils) {
this.repo = repo;
this.persistUtils = persistUtils;
}
@POST
@Timed
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response create(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
@Valid CreateNotificationMethodCommand command) {
command.validate();
NotificationMethod notificationMethod =
Links.hydrate(repo.create(tenantId, command.name, command.type, command.address), uriInfo,
false);
return Response.created(URI.create(notificationMethod.getId())).entity(notificationMethod)
.build();
}
@GET
@Timed
@Produces(MediaType.APPLICATION_JSON)
public Object list(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
@QueryParam("offset") String offset,
@QueryParam("limit") String limit) throws UnsupportedEncodingException {
return Links.paginate(this.persistUtils.getLimit(limit),
Links.hydrate(repo.find(tenantId, offset,
this.persistUtils.getLimit(limit)), uriInfo),
uriInfo);
}
@GET
@Timed
@Path("/{notification_method_id}")
@Produces(MediaType.APPLICATION_JSON)
public NotificationMethod get(@Context UriInfo uriInfo,
@HeaderParam("X-Tenant-Id") String tenantId,
@PathParam("notification_method_id") String notificationMethodId) {
return Links.hydrate(repo.findById(tenantId, notificationMethodId), uriInfo, true);
}
@PUT
@Timed
@Path("/{notification_method_id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public NotificationMethod update(@Context UriInfo uriInfo,
@HeaderParam("X-Tenant-Id") String tenantId,
@PathParam("notification_method_id") String notificationMethodId,
@Valid CreateNotificationMethodCommand command) {
command.validate();
return Links.hydrate(
repo.update(tenantId, notificationMethodId, command.name, command.type, command.address),
uriInfo, true);
}
@DELETE
@Timed
@Path("/{notification_method_id}")
public void delete(@HeaderParam("X-Tenant-Id") String tenantId,
@PathParam("notification_method_id") String notificationMethodId) {
repo.deleteById(tenantId, notificationMethodId);
}
}

View File

@ -1,97 +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 monasca.api.resource;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.codahale.metrics.annotation.Timed;
import org.joda.time.DateTime;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
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.UriInfo;
import monasca.api.app.validation.Validation;
import monasca.api.domain.model.statistic.StatisticRepo;
import monasca.api.infrastructure.persistence.PersistUtils;
// import monasca.common.util.stats.Statistics;
/**
* Statistics resource implementation.
*/
@Path("/v2.0/metrics/statistics")
public class StatisticResource {
private static final Splitter COMMA_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults();
private final StatisticRepo repo;
private final PersistUtils persistUtils;
@Inject
public StatisticResource(StatisticRepo repo, PersistUtils persistUtils) {
this.repo = repo;
this.persistUtils = persistUtils;
}
@GET
@Timed
@Produces(MediaType.APPLICATION_JSON)
public Object get(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId,
@QueryParam("name") String name,
@QueryParam("dimensions") String dimensionsStr,
@QueryParam("start_time") String startTimeStr,
@QueryParam("end_time") String endTimeStr,
@QueryParam("statistics") String statisticsStr,
@DefaultValue("300")
@QueryParam("period") String periodStr,
@QueryParam("offset") String offset,
@QueryParam("limit") String limit,
@QueryParam("merge_metrics") Boolean mergeMetricsFlag) throws Exception {
// Validate query parameters
DateTime startTime = Validation.parseAndValidateDate(startTimeStr, "start_time", true);
DateTime endTime = Validation.parseAndValidateDate(endTimeStr, "end_time", false);
Validation.validateTimes(startTime, endTime);
Validation.validateNotNullOrEmpty(statisticsStr, "statistics");
int period = Validation.parseAndValidateNumber(periodStr, "period");
List<String> statistics =
Validation.parseValidateAndNormalizeStatistics(COMMA_SPLITTER.split(statisticsStr));
Map<String, String> dimensions =
Strings.isNullOrEmpty(dimensionsStr) ? null : Validation.parseAndValidateNameAndDimensions(
name, dimensionsStr, true);
return Links.paginateStatistics(this.persistUtils.getLimit(limit),
repo.find(tenantId, name, dimensions, startTime, endTime,
statistics, period, offset,
this.persistUtils.getLimit(limit),
mergeMetricsFlag),
uriInfo);
}
}

View File

@ -1,66 +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 monasca.api.resource;
import com.codahale.metrics.annotation.Timed;
import java.io.UnsupportedEncodingException;
import javax.inject.Inject;
import javax.ws.rs.GET;
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.UriInfo;
import monasca.api.domain.model.version.Version;
import monasca.api.domain.model.version.VersionRepo;
import monasca.api.infrastructure.persistence.PersistUtils;
/**
* Version resource implementation.
*/
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
public class VersionResource {
private final VersionRepo repository;
private final PersistUtils persistUtils;
@Inject
public VersionResource(VersionRepo repository,
PersistUtils persistUtils) {
this.repository = repository;
this.persistUtils = persistUtils;
}
@GET
@Timed
public Object list(@Context UriInfo uriInfo,
@QueryParam("offset") String offset,
@QueryParam("limit") String limit) throws UnsupportedEncodingException {
return Links.paginate(this.persistUtils.getLimit(limit),
Links.hydrate(repository.find(), uriInfo), uriInfo);
}
@GET
@Timed
@Path("{version_id}")
public Version get(@Context UriInfo uriInfo, @PathParam("version_id") String versionId) {
return Links.hydrate(repository.findById(versionId), uriInfo, true);
}
}

View File

@ -1,27 +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 monasca.api.resource.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.HttpMethod;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("PATCH")
public @interface PATCH {
}

View File

@ -1,40 +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 monasca.api.resource.exception;
import io.dropwizard.jersey.validation.ValidationErrorMessage;
import javax.validation.ConstraintViolationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import monasca.api.resource.exception.Exceptions.FaultType;
@Provider
public class ConstraintViolationExceptionMapper implements
ExceptionMapper<ConstraintViolationException> {
private static final int UNPROCESSABLE_ENTITY = 422;
@Override
public Response toResponse(ConstraintViolationException exception) {
final ValidationErrorMessage message =
new ValidationErrorMessage(exception.getConstraintViolations());
String msg =
message.getErrors().isEmpty() ? exception.getMessage() : message.getErrors().toString();
return Response.status(UNPROCESSABLE_ENTITY).type(MediaType.APPLICATION_JSON)
.entity(Exceptions.buildLoggedErrorMessage(FaultType.UNPROCESSABLE_ENTITY, msg)).build();
}
}

View File

@ -1,32 +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 monasca.api.resource.exception;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import monasca.api.domain.exception.EntityExistsException;
import monasca.api.resource.exception.Exceptions.FaultType;
@Provider
public class EntityExistsExceptionMapper implements ExceptionMapper<EntityExistsException> {
@Override
public Response toResponse(EntityExistsException e) {
return Response.status(Status.CONFLICT).type(MediaType.APPLICATION_JSON)
.entity(Exceptions.buildLoggedErrorMessage(FaultType.CONFLICT, e.getMessage())).build();
}
}

View File

@ -1,32 +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 monasca.api.resource.exception;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import monasca.api.domain.exception.EntityNotFoundException;
import monasca.api.resource.exception.Exceptions.FaultType;
@Provider
public class EntityNotFoundExceptionMapper implements ExceptionMapper<EntityNotFoundException> {
@Override
public Response toResponse(EntityNotFoundException e) {
return Response.status(Status.NOT_FOUND).type(MediaType.APPLICATION_JSON)
.entity(Exceptions.buildLoggedErrorMessage(FaultType.NOT_FOUND, e.getMessage())).build();
}
}

View File

@ -1,36 +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 monasca.api.resource.exception;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
public class ErrorMessage {
public int code;
public String message;
public String details;
@JsonProperty("internal_code")
public String internalCode;
ErrorMessage() {}
public ErrorMessage(int code, String message, String details, String internalCode) {
Preconditions.checkNotNull(internalCode, "internalCode");
this.code = code;
this.message = message == null ? "" : message;
this.details = details == null ? "" : details;
this.internalCode = internalCode;
}
}

View File

@ -1,164 +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 monasca.api.resource.exception;
import java.util.Random;
import javax.annotation.Nullable;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.google.common.base.Splitter;
/**
* Exception factory methods.
*/
public final class Exceptions {
private static final Logger LOG = LoggerFactory.getLogger(Exceptions.class);
private static final ObjectMapper OBJECT_MAPPER;
private static final Splitter LINE_SPLITTER = Splitter.on("\n").trimResults();
private static final Random RANDOM = new Random();
static {
OBJECT_MAPPER = new ObjectMapper();
OBJECT_MAPPER
.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
}
public enum FaultType {
SERVER_ERROR(Status.INTERNAL_SERVER_ERROR, true), BAD_REQUEST(Status.BAD_REQUEST, true), UNAUTHORIZED(
Status.UNAUTHORIZED, false), NOT_FOUND(Status.NOT_FOUND, true), CONFLICT(Status.CONFLICT,
true), UNPROCESSABLE_ENTITY(422, true), FORBIDDEN(Status.FORBIDDEN, true);
public final int statusCode;
public final boolean loggable;
FaultType(int statusCode, boolean loggable) {
this.statusCode = statusCode;
this.loggable = loggable;
}
FaultType(Status status, boolean loggable) {
this.statusCode = status.getStatusCode();
this.loggable = loggable;
}
@Override
public String toString() {
return name().toLowerCase();
}
}
private static class WebAppException extends WebApplicationException {
private static final long serialVersionUID = 1L;
public WebAppException(FaultType faultType, String message) {
super(Response.status(faultType.statusCode).entity(message).type(MediaType.APPLICATION_JSON)
.build());
}
}
private Exceptions() {}
public static WebApplicationException badRequest(String msg, Object... args) {
return new WebAppException(FaultType.BAD_REQUEST, buildLoggedErrorMessage(
FaultType.BAD_REQUEST, msg, args));
}
/**
* Builds and returns an error message containing an error code, and logs the message with the
* corresponding error code.
*/
public static String buildLoggedErrorMessage(FaultType faultType, String message, Object... args) {
return buildLoggedErrorMessage(faultType,
args == null || args.length == 0 ? message : String.format(message, args), null, null);
}
/**
* Builds and returns an error message containing an error code, and logs the message with the
* corresponding error code.
*/
public static String buildLoggedErrorMessage(FaultType faultType, String message,
@Nullable String details, @Nullable Throwable exception) {
String errorCode = Long.toHexString(RANDOM.nextLong());
if (faultType.loggable) {
String withoutDetails = "{} {} - {}";
String withDetails = "{} {} - {} {}";
if (details == null) {
if (exception == null)
LOG.error(withoutDetails, faultType.name(), errorCode, message);
else
LOG.error(withoutDetails, faultType.name(), errorCode, message, exception);
} else {
if (exception == null)
LOG.error(withDetails, faultType.name(), errorCode, message, details);
else
LOG.error(withDetails, faultType.name(), errorCode, message, details, exception);
}
}
try {
StringBuilder str = new StringBuilder("{\"");
str.append(faultType.toString());
str.append("\":");
str.append(OBJECT_MAPPER.writeValueAsString(new ErrorMessage(faultType.statusCode, message,
details, errorCode)));
str.append("}");
return str.toString();
} catch (JsonProcessingException bestEffort) {
return null;
}
}
public static WebApplicationException forbidden(String msg, Object... args) {
return new WebAppException(FaultType.FORBIDDEN, buildLoggedErrorMessage(FaultType.FORBIDDEN,
msg, args));
}
/**
* Returns the first line off of a stacktrace message.
*/
public static String stripLocationFromStacktrace(String message) {
for (String s : LINE_SPLITTER.split(message))
return s;
return message;
}
/**
* Indicates that the content of a a POSTed request entity is invalid.
*/
public static WebApplicationException unprocessableEntity(String msg, Object... args) {
return new WebAppException(FaultType.UNPROCESSABLE_ENTITY, buildLoggedErrorMessage(
FaultType.UNPROCESSABLE_ENTITY, msg, args));
}
/**
* Indicates that the content of a a POSTed request entity is invalid.
*/
public static WebApplicationException unprocessableEntityDetails(String msg, String details,
Exception exception) {
return new WebAppException(FaultType.UNPROCESSABLE_ENTITY, buildLoggedErrorMessage(
FaultType.UNPROCESSABLE_ENTITY, msg, details, exception));
}
}

View File

@ -1,31 +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 monasca.api.resource.exception;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import monasca.api.resource.exception.Exceptions.FaultType;
@Provider
public class IllegalArgumentExceptionMapper implements ExceptionMapper<IllegalArgumentException> {
@Override
public Response toResponse(IllegalArgumentException e) {
return Response.status(Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON)
.entity(Exceptions.buildLoggedErrorMessage(FaultType.BAD_REQUEST, e.getMessage())).build();
}
}

View File

@ -1,31 +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 monasca.api.resource.exception;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import monasca.api.domain.exception.InvalidEntityException;
import monasca.api.resource.exception.Exceptions.FaultType;
@Provider
public class InvalidEntityExceptionMapper implements ExceptionMapper<InvalidEntityException> {
@Override
public Response toResponse(InvalidEntityException e) {
return Response.status(FaultType.BAD_REQUEST.statusCode).type(MediaType.APPLICATION_JSON)
.entity(Exceptions.buildLoggedErrorMessage(FaultType.BAD_REQUEST, e.getMessage())).build();
}
}

View File

@ -1,39 +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 monasca.api.resource.exception;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.databind.JsonMappingException;
import monasca.api.resource.exception.Exceptions.FaultType;
/**
* Adapted from Dropwizard's JsonMappingExceptionManager.
*/
@Provider
public class JsonMappingExceptionManager implements ExceptionMapper<JsonMappingException> {
@Override
public Response toResponse(JsonMappingException exception) {
return Response
.status(FaultType.BAD_REQUEST.statusCode)
.type(MediaType.APPLICATION_JSON)
.entity(
Exceptions.buildLoggedErrorMessage(FaultType.BAD_REQUEST,
"Unable to process the provided JSON",
Exceptions.stripLocationFromStacktrace(exception.getMessage()), null)).build();
}
}

View File

@ -1,69 +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 monasca.api.resource.exception;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonProcessingException;
import monasca.api.resource.exception.Exceptions.FaultType;
/**
* Adapted from Dropwizard's JsonProcessingExceptionMapper.
*/
@Provider
public class JsonProcessingExceptionMapper implements ExceptionMapper<JsonProcessingException> {
@Override
public Response toResponse(JsonProcessingException exception) {
/*
* If the error is in the JSON generation, it's a server error.
*/
if (exception instanceof JsonGenerationException)
return Response
.status(Status.INTERNAL_SERVER_ERROR)
.type(MediaType.APPLICATION_JSON)
.entity(
Exceptions.buildLoggedErrorMessage(FaultType.SERVER_ERROR, "Error generating JSON",
null, exception)).build();
final String message = exception.getMessage();
/*
* If we can't deserialize the JSON because someone forgot a no-arg constructor, it's a server
* error and we should inform the developer.
*/
if (message.startsWith("No suitable constructor found"))
return Response
.status(Status.INTERNAL_SERVER_ERROR)
.type(MediaType.APPLICATION_JSON)
.entity(
Exceptions.buildLoggedErrorMessage(FaultType.SERVER_ERROR,
"Unable to deserialize the provided JSON", null, exception)).build();
/*
* Otherwise, it's those pesky users.
*/
return Response
.status(Status.BAD_REQUEST)
.type(MediaType.APPLICATION_JSON)
.entity(
Exceptions.buildLoggedErrorMessage(FaultType.BAD_REQUEST,
"Unable to process the provided JSON",
Exceptions.stripLocationFromStacktrace(message), exception)).build();
}
}

View File

@ -1,46 +0,0 @@
/*
* Copyright (c) 2015 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 monasca.api.resource.exception;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import monasca.api.domain.exception.MultipleMetricsException;
@Provider
public class MultipleMetricsExceptionMapper implements ExceptionMapper<MultipleMetricsException> {
private static final String
MULTIPLE_METRICS_ERROR_MSG =
"Found multiple metrics matching metric name and dimensions. "
+ "Please refine your search criteria using a unique metric name or additional dimensions. "
+ "Alternatively, you may specify 'merge_metrics=true' as a query param to combine "
+ "all metrics matching search criteria into a single series.";
@Override
public Response toResponse(MultipleMetricsException exception) {
String details = String.format("search criteria: {metric name: %s, dimensions: %s}",
exception.getMetricName(), exception.getDimensions());
return Response.status(Response.Status.CONFLICT).type(MediaType.APPLICATION_JSON).entity(
Exceptions
.buildLoggedErrorMessage(Exceptions.FaultType.CONFLICT, MULTIPLE_METRICS_ERROR_MSG,
details, null)).build();
}
}

View File

@ -1,44 +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 monasca.api.resource.exception;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import monasca.api.resource.exception.Exceptions.FaultType;
/**
* Adapted from Dropwizard's LoggingExceptionMapper.
*
* @param <E> Exception type
*/
@Provider
public class ThrowableExceptionMapper<E extends Throwable> implements ExceptionMapper<E> {
@Override
public Response toResponse(E exception) {
if (exception instanceof WebApplicationException)
return ((WebApplicationException) exception).getResponse();
return Response
.status(Status.INTERNAL_SERVER_ERROR)
.type(MediaType.APPLICATION_JSON)
.entity(
Exceptions.buildLoggedErrorMessage(FaultType.SERVER_ERROR,
"An internal server error occurred", null, exception)).build();
}
}

View File

@ -1,47 +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 monasca.api.resource.serialization;
import java.io.IOException;
import java.util.Collections;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import monasca.common.model.alarm.AlarmSubExpression;
public class SubAlarmExpressionSerializer extends JsonSerializer<AlarmSubExpression> {
@Override
public void serialize(AlarmSubExpression value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeStringField("function", value.getFunction().name());
jgen.writeStringField("metric_name", value.getMetricDefinition().name);
jgen.writeObjectField(
"dimensions",
value.getMetricDefinition().dimensions == null ? Collections.emptyMap() : value
.getMetricDefinition().dimensions);
jgen.writeStringField("operator", value.getOperator().name());
jgen.writeNumberField("threshold", value.getThreshold());
jgen.writeNumberField("period", value.getPeriod());
jgen.writeNumberField("periods", value.getPeriods());
jgen.writeEndObject();
}
@Override
public Class<AlarmSubExpression> handledType() {
return AlarmSubExpression.class;
}
}

Some files were not shown because too many files have changed in this diff Show More