Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fdfed7554 | ||
|
|
e90340be09 | ||
|
|
9770dce1cd | ||
|
|
8f98199a37 | ||
|
|
425616d536 | ||
|
|
158ee4fd55 | ||
|
|
89e8fd7ebd | ||
|
|
cb5794491a | ||
|
|
8b4f0d5764 | ||
|
|
c90b86c4c9 | ||
|
|
f104d02dc6 | ||
|
|
ac85ad1aa7 | ||
|
|
21f3c20a5f | ||
|
|
b43d877035 | ||
|
|
8f46ee052c | ||
|
|
6cda9c4a60 | ||
|
|
cfc3e491d6 | ||
|
|
23668b294f | ||
|
|
beceb678ea | ||
|
|
4257e28233 | ||
|
|
fb007c170e | ||
|
|
cabeb2c0d7 | ||
|
|
e4a2b011a4 | ||
|
|
8f6dfd1644 | ||
|
|
33fb3dc857 | ||
|
|
186edeb0f2 | ||
|
|
a034223376 | ||
|
|
9c75c75aa5 | ||
|
|
c8646a8d59 | ||
|
|
b28cb4451b | ||
|
|
a4b9a1034c | ||
|
|
12e64316eb | ||
|
|
a2389435d4 | ||
|
|
6a0455b1cd | ||
|
|
7abc9e68e4 | ||
|
|
e45dcb0450 | ||
|
|
a8ce05000e | ||
|
|
747d3e7ad1 | ||
|
|
fcf0ec0b87 | ||
|
|
976c6da07c | ||
|
|
ed96a0a8e8 | ||
|
|
d5c3bbbcc5 | ||
|
|
ec1ac9af73 | ||
|
|
2a2d11d01b | ||
|
|
14eba0fb93 | ||
|
|
418afe8f87 | ||
|
|
2b39ec3ab5 | ||
|
|
2ff895b147 | ||
|
|
16a6223c48 | ||
|
|
e928ac3746 | ||
|
|
8f5d54ebd4 | ||
|
|
123f63972e | ||
|
|
dd30d54ed9 | ||
|
|
d37bf1c824 | ||
|
|
6cd4a8bd67 | ||
|
|
1fa71e0c6a | ||
|
|
c5efeb5be9 | ||
|
|
2c8fa5da44 | ||
|
|
9cbc94eb58 | ||
|
|
4ce4482764 | ||
|
|
49a8d970fe | ||
|
|
1be6f24676 | ||
|
|
82b94f1a71 | ||
|
|
019c5789ce | ||
|
|
4336defc14 | ||
|
|
712a2100bb | ||
|
|
bc46f54604 | ||
|
|
1b713e7874 | ||
|
|
3f5dfd2219 | ||
|
|
ea707fda42 | ||
|
|
b245be476a | ||
|
|
9619530814 | ||
|
|
adf617e59f | ||
|
|
3f49855840 | ||
|
|
053d15c559 | ||
|
|
b3a479636b | ||
|
|
8539986faf | ||
|
|
cbdf28c510 | ||
|
|
670cc7769d | ||
|
|
22a9be3ba2 | ||
|
|
2836b03366 | ||
|
|
64bde2189e | ||
|
|
4d41c8e19a | ||
|
|
08a095e349 | ||
|
|
fc0f2b8356 | ||
|
|
e9c0f63478 | ||
|
|
9c01d00686 | ||
|
|
dd8d379292 | ||
|
|
1acd5ce5cb | ||
|
|
aa3ac901b6 | ||
|
|
2c6270f23e | ||
|
|
f6728b4e8d | ||
|
|
fb0590d778 | ||
|
|
f94ec78c66 | ||
|
|
3fddbb4c6f | ||
|
|
e841ac90b8 | ||
|
|
203a97d7db | ||
|
|
4d19538c77 | ||
|
|
e83356cb74 | ||
|
|
5f1beb6045 | ||
|
|
12ee77386b | ||
|
|
accbb2af9a | ||
|
|
041cee1a80 | ||
|
|
f8efdbf240 | ||
|
|
ca4155be6f | ||
|
|
b3c7b143e0 | ||
|
|
42d31efff8 | ||
|
|
56e90ef693 | ||
|
|
6345efa996 | ||
|
|
701f841cfe | ||
|
|
4b6de88e25 | ||
|
|
de9b0df49e | ||
|
|
b8418e4e19 | ||
|
|
d5cec191e2 | ||
|
|
13feefde31 | ||
|
|
86c9d8835d | ||
|
|
b8da5152e0 | ||
|
|
e73564c578 | ||
|
|
f42696ddfc | ||
|
|
7e3a85050d |
No files matched your search
@@ -4,3 +4,6 @@
|
||||
target/
|
||||
.idea/
|
||||
*.iml
|
||||
logs/
|
||||
server/pki/
|
||||
|
||||
@@ -3,3 +3,4 @@ Eric Newton ([email protected])
|
||||
Jim Klucar ([email protected])
|
||||
Charlie Williams ([email protected])
|
||||
Drew Farris ([email protected])
|
||||
Brian Loss ([email protected])
|
||||
@@ -0,0 +1,2 @@
|
||||
This product includes Google Flatbuffers (https://google.github.io/flatbuffers/)
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
@@ -1,180 +1,8 @@
|
||||
# Timely
|
||||
---
|
||||

|
||||
|
||||
Timely is a time series database application that provides secure access to time series data. Timely is written in Java and designed to work with [Apache Accumulo] (http://accumulo.apache.org/) and [Grafana] (http://www.grafana.org).
|
||||
[![Apache License][li]][ll]
|
||||
|
||||
# Design
|
||||
---
|
||||
|
||||
Being big users of Apache Accumulo, when we required a time series database application we looked to use software that was compatible with it. Specifically, we started with [OpenTSDB] (http://www.opentsdb.net) and Eric Newton's [Accumulo-OpenTSDB] (https://github.com/ericnewton/accumulo-opentsdb) project to bridge them together and Grafana for visualization. We ultimately ran into some issues that we could not work around. After much discussion we decided to replace OpenTSDB with something more native to Apache Accumulo. The implementation of Timely started with the following constraints and assumptions:
|
||||
|
||||
1. We need to show progress quickly.
|
||||
2. We started with Grafana, so we didn't need our own user interface.
|
||||
3. We already had experience with OpenTSDB, so we started with its API.
|
||||
4. Space is cheap, ingest and query speed are more important.
|
||||
5. Metrics will be kept short term.
|
||||
6. The number of tags used for each metric will be small \( < 10 \).
|
||||
7. We can create a better API and Grafana plugin at a later time.
|
||||
|
||||
## Building
|
||||
|
||||
Timely uses Maven for compiling source, running tests, and packaging distributions. Below is a set of useful Maven lifecyles that we use:
|
||||
|
||||
Command | Description
|
||||
--------|------------
|
||||
mvn compile | Compiles and formats the source
|
||||
mvn test | Runs unit tests
|
||||
mvn package | Runs findbugs and creates a distribution
|
||||
mvn verify | Runs integration tests
|
||||
mvn verify site | Creates the site
|
||||
|
||||
The [CollectD] (http://collectd.org/) plugins require that CollectD is installed locally as the `/usr/share/collectd/java/collectd-api.jar` file is a dependency.
|
||||
|
||||
## Deployment
|
||||
|
||||
The Timely server requires a Java 8 runtime. Timely utilizes iterators for Apache Accumulo, so your Accumulo instance will need to be run with Java 8 also.
|
||||
|
||||
Create a distribution and untar it somewhere. Modify the `conf/timely.properties` file appropriately. Then, copy the `timely-server` and `commons-lang3` jar files to your Accumulo tservers. Next, launch Timely by running the `bin/timely-server.sh` script.
|
||||
|
||||
If you just want to kick the tires without having to install and setup Apache Hadoop and Apache Accumulo, then you can start Timely with the `bin/timely-standalone.sh` script. This will start an Accumulo MiniCluster in the background, but be aware that the standalone instance will not save your metric data across restarts.
|
||||
|
||||
## API
|
||||
|
||||
The current Timely API is compatible with a subset of the OpenTSDB API. We currently support the OpenTSDB TCP put API and we have partially implemented the following HTTP operations that the Grafana OpenTSDB datasource uses:
|
||||
|
||||
* /api/aggregators
|
||||
* /api/query
|
||||
* /api/search/lookup
|
||||
* /api/suggest
|
||||
|
||||
We also provide the following additional endpoints:
|
||||
|
||||
* /login
|
||||
|
||||
This endpoint should be used for non-anonymous access. It supports a POST request with username and password, or a GET request with SSL mutual authentication.
|
||||
|
||||
* /api/metrics
|
||||
|
||||
This endpoint reports the metric names and tags that have been pushed to Timely. Eviction of this information is configurable, so it may reflect metrics that have been reported in the past but are no longer being actively reported. This could be useful in creating graphs in Grafana (so you won't have to go digging in the `meta` table in Accumulo).
|
||||
|
||||
## Storage Format
|
||||
|
||||
Metrics sent to Timely are stored in two Accumulo tables, `meta` and `metrics`. The `meta` table stores information about the metric names and tags. It's format is:
|
||||
|
||||
Row | ColumnFamily | ColumnQualifier | Value
|
||||
----|:-------------|:----------------|:-----
|
||||
m:metric | | |
|
||||
t:metric | tagKey | |
|
||||
v:metric | tagKey | tagValue |
|
||||
|
||||
The `metrics` table stores the datapoints using the following format:
|
||||
|
||||
Row | ColumnFamily | ColumnQualifier | Value
|
||||
----|:-------------|:----------------|:-----
|
||||
metric\timestamp | tagKey=tagValue | tagKey=tagValue,tagKey=tagValue,... | metricValue
|
||||
|
||||
As an example, if you sent the following metric to the put api
|
||||
|
||||
```
|
||||
put sys.cpu.user 1447879348291 2.0 rack=r001 host=r001n01 instance=0
|
||||
```
|
||||
|
||||
it would get stored in the following manner in the `meta` table:
|
||||
|
||||
Row | ColumnFamily | ColumnQualifier | Value
|
||||
----|:-------------|:----------------|:-----
|
||||
m:sys.cpu.user | | |
|
||||
t:sys.cpu.user | host | |
|
||||
t:sys.cpu.user | instance | |
|
||||
t:sys.cpu.user | rack | |
|
||||
v:sys.cpu.user | host | r001n01 |
|
||||
v:sys.cpu.user | instance | 0 |
|
||||
v:sys.cpu.user | rack | r001 |
|
||||
|
||||
and in the following manner in the `metrics` table
|
||||
|
||||
Row | ColumnFamily | ColumnQualifier | Value
|
||||
----|:-------------|:----------------|:-----
|
||||
sys.cpu.user\1447879348291 | host=r001n01 | instance=0,rack=r001 | 2.0
|
||||
sys.cpu.user\1447879348291 | instance=0 | host=r001n01,rack=r001 | 2.0
|
||||
sys.cpu.user\1447879348291 | rack=r001 | host=r001n01,instance=0 | 2.0
|
||||
|
||||
## Configuration
|
||||
|
||||
The `NUM_SERVER_THREADS` variable in the `timely-server.sh` script controls how many threads are used in the Netty event group for TCP and HTTP operations. The TCP and HTTP groups use a different event group, so if you set the value to 8, then you will have 8 threads for TCP operations and 8 threads for HTTP operations. The properties file in the `conf` directory supports the following properties:
|
||||
|
||||
Property | Description | Default Value
|
||||
:--------|:------------|:-------------
|
||||
timely.ip | The ip address where the Timely server is running |
|
||||
timely.port.put | The port that will be used for processing put requests |
|
||||
timely.port.query | The port that will be used for processing query requests |
|
||||
timely.instance\_name | The name of your Accumulo instance |
|
||||
timely.zookeepers | The list of Zookeepers for your Accumulo instance |
|
||||
timely.username | The username that Timely will use to connect to Accumulo |
|
||||
timely.password | The password of the Accumulo user |
|
||||
timely.table | The name of the metrics table | timely.metrics
|
||||
timely.meta | The name of the meta table | timely.meta
|
||||
timely.write.latency | The Accumulo BatchWriter latency | 5s
|
||||
timely.write.threads | The Accumulo BatchWriter number of threads | [default](https://github.com/apache/accumulo/blob/master/core/src/main/java/org/apache/accumulo/core/client/BatchWriterConfig.java#L49)
|
||||
timely.write.buffer.size | The Accumulo BatchWriter buffer size | [default](https://github.com/apache/accumulo/blob/master/core/src/main/java/org/apache/accumulo/core/client/BatchWriterConfig.java#L40)
|
||||
timely.metric.age.off.days | The number of days to keep metrics | 7
|
||||
timely.cors.allow.any.origin | Allow any origin in cross origin requests (true/false) | false
|
||||
timely.cors.allow.null.origin | Allow null origins in cross origin requests (true/false) | false
|
||||
timely.cors.allowed.origins | List of allowed origins in cross origin requests (can be null or comma separated list)
|
||||
timely.cors.allowed.methods | List of allowed methods for cross origin requests | <ul><li>DELETE</li><li>GET</li><li>HEAD</li><li>OPTIONS</li><li>PUT</li><li>POST</li></ul>
|
||||
timely.cors.allowed.headers | Comma separated list of allowed HTTP headers for cross origin requests | content-type
|
||||
timely.cors.allow.credentials | Allow credentials to be passed in cross origin requests (true/false) | true
|
||||
timely.scanner.threads | Number of BatchScanner threads to be used in a query | 4
|
||||
timely.metrics.report.tags.ignored | Comma separated list of tags which will not be shown in the /api/metrics response |
|
||||
timely.meta.cache.expiration.minutes | Number of minutes after which unaccessed meta information will be purged from the meta cache | 60
|
||||
timely.meta.cache.initial.capacity | Initial capacity of the meta cache | 2000
|
||||
timely.meta.cache.max.capacity | Maximum capacity of the meta cache | 10000
|
||||
timely.ssl.certificate.file | Public certificate to use for the Timely server (x509 pem format) |
|
||||
timely.ssl.key.file | Private key to use for the Timely server (in pkcs8 format) |
|
||||
timely.ssl.key.pass | Password to the private key |
|
||||
timely.ssl.use.generated.keypair | Use a generated certificate/key pair - useful for testing | false
|
||||
timely.ssl.trust.store.file | Certificate trust store (a concatenated list of trusted CA x509 pem certificates) |
|
||||
timely.ssl.use.openssl | Use OpenSSL (vs JDK SSL) | true
|
||||
timely.ssl.use.ciphers | List of allowed SSL ciphers | see Configuration.java
|
||||
timely.session.max.age | Setting for max age of session cookie (in seconds) | 86400
|
||||
timely.http.address | Address for the Timely server, used for the session cookie domain |
|
||||
grafana.http.address | Address for the Grafana server |
|
||||
timely.allow.anonymous.access | Allow anonymous access | false
|
||||
timely.visibility.cache.expiration.minutes | Column Visibility Cache Expiration (minutes) | 60
|
||||
timely.visibility.cache.initial.capacity | Column Visibility Cache Initial Capacity | 2000
|
||||
timely.visibility.cache.max.capacity | Column Visibility Cache Max Capacity | 10000
|
||||
|
||||
## Security
|
||||
|
||||
Timely allows users to optionally label their data using Accumulo column visibility expressions. To enable this feature, users should put the expressions in a tag named `viz`. Timely will [flatten] (http://accumulo.apache.org/1.7/apidocs/org/apache/accumulo/core/security/ColumnVisibility.html#flatten%28%29) this expression and store it in the column visibility of the data point in the metrics table. Column visibilities are *not* stored in the meta table, so anyone can see metric names, tag names, and tag values.
|
||||
|
||||
Timely provides HTTPS access to the query endpoints. It is possible to allow anonymous access to these endpoints which will allow anyone to see unlabeled data. Timely uses Spring Security to configure user authentication and user role information. Users must call the `/login` endpoint for authentication and Timely will respond by setting a HTTP cookie with a session id. The response to a successful login will be a temporary redirect (HTTP 307) to the configured address for the Grafana server. Grafana itself must be configured to use https for this to work properly. For more information see the [Quick Start] (quick-start/QUICK_START.md) documentation.
|
||||
|
||||
## Tuning
|
||||
|
||||
1. Each thread in the Timely server that is used for processing TCP put operations has its own BatchWriter. Each BatchWriter honors the `timely.write.latency` and `timely.write.threads` configuration property, but the buffer size for each BatchWriter is `timely.write.buffer.size` divided by the number of threads. For example, if you have 8 threads processing put operations and the following settings, then you will have 8 BatchWriters each using 2 threads with a 30s latency and a maximum buffer size of 128M:
|
||||
|
||||
```
|
||||
timely.write.latency=30s
|
||||
timely.write.threads=2
|
||||
timely.write.buffer.size=1G
|
||||
```
|
||||
|
||||
2. The `timely.scanner.threads` property is used for BatchScanners on a per query basis. If you set this to 32 and have 8 threads processing HTTP operations, then you might have 256 threads concurrently querying your tablet servers. Be sure to set your ulimits appropriately.
|
||||
|
||||
3. The Timely server contains an object called the meta cache, which is a cache of the keys that would be inserted into the meta table if they did not already exist. This cache is used to reduce the insert load of duplicate keys into the meta table and to serve up data to the `/api/metrics` endpoint. The meta cache is an object that supports eviction based on last access time and can be tuned with the `timely.meta.cache.*` properties.
|
||||
|
||||
|
||||
## NOTES
|
||||
|
||||
1. Not shown in the examples is the encoding of the row and value. You can apply the Timely formatter to your metrics table using the Accumulo shell command:
|
||||
```
|
||||
config -t timely.metrics -s table.formatter=timely.util.TimelyMetricsFormatter
|
||||
```
|
||||
|
||||
2. You can create split points for your metrics table based on the 'm' column family in the meta table.
|
||||
|
||||
3. You can lower the `table.scan.max.memory` property on your metrics table in an attempt to get data back faster from the tablet servers.
|
||||
|
||||
4. If you don't mind losing some metric data in the event of an Accumulo tablet server death, you can set the `table.walog.enabled` property to false and the `table.durability` property to none on your metrics table. This should speed up ingest a little.
|
||||
Timely is a time series database application that provides secure access to time series data. Timely is written in Java and designed to work with [Apache Accumulo] (http://accumulo.apache.org/) and [Grafana] (http://www.grafana.org). Documentation is located [here] (https://nationalsecurityagency.github.io/timely/).
|
||||
|
||||
[li]: http://img.shields.io/badge/license-ASL-blue.svg
|
||||
[ll]: https://www.apache.org/licenses/LICENSE-2.0
|
||||
@@ -0,0 +1 @@
|
||||
/target/
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.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>
|
||||
<artifactId>timely-client</artifactId>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.3</version>
|
||||
</parent>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely-common</artifactId>
|
||||
<version>${parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>${version.findbugs-maven}</version>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>${version.jacoco}</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<id>JaCoCo Unit Test Coverage</id>
|
||||
<configuration>
|
||||
<dataFile>${project.build.directory}/coverage/jacoco-ut.exec</dataFile>
|
||||
</configuration>
|
||||
<reports>
|
||||
<report>report</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
<reportSet>
|
||||
<id>JaCoCo Integration Test Coverage</id>
|
||||
<configuration>
|
||||
<dataFile>${project.build.directory}/coverage/jacoco-it.exec</dataFile>
|
||||
</configuration>
|
||||
<reports>
|
||||
<report>report-integration</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
</project>
|
||||
@@ -0,0 +1,182 @@
|
||||
package timely.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.*;
|
||||
import timely.model.parse.TagParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A Metric consists of a metric name, tags, and a Value
|
||||
*
|
||||
* Json has been flattened to reduce overhead. Example:
|
||||
* {"name":"m1","timestamp":1,"measure":1.0,"tags":[{"k1":"v1"}]}
|
||||
*
|
||||
*/
|
||||
@JsonRootName("metric")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonPropertyOrder({ "name" })
|
||||
public class Metric {
|
||||
|
||||
private String name;
|
||||
private List<Tag> tags;
|
||||
private Value value;
|
||||
|
||||
public Metric() {
|
||||
tags = new ArrayList<>();
|
||||
value = new Value();
|
||||
}
|
||||
|
||||
public Metric(Metric other) {
|
||||
this();
|
||||
this.name = other.getName();
|
||||
this.tags.addAll(other.getTags());
|
||||
this.value = new Value(other.getValue());
|
||||
}
|
||||
|
||||
public Metric(String name, long timestamp, double measure) {
|
||||
this();
|
||||
this.name = name;
|
||||
this.value.setTimestamp(timestamp);
|
||||
this.value.setMeasure(measure);
|
||||
}
|
||||
|
||||
public Metric(String name, long timestamp, double measure, List<Tag> tags) {
|
||||
this(name, timestamp, measure);
|
||||
this.setTags(tags);
|
||||
}
|
||||
|
||||
public Metric(String name, List<Tag> tags) {
|
||||
this();
|
||||
this.name = name;
|
||||
this.setTags(tags);
|
||||
}
|
||||
|
||||
public static Builder newBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
@JsonGetter("name")
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@JsonGetter("tags")
|
||||
public List<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(List<Tag> tags) {
|
||||
this.tags.addAll(tags);
|
||||
}
|
||||
|
||||
public void addTag(Tag tag) {
|
||||
tags.add(new Tag(tag));
|
||||
}
|
||||
|
||||
@JsonUnwrapped
|
||||
public Value getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Value value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Collections.sort(tags);
|
||||
return "Metric{" + "name='" + name + '\'' + ", tags=" + tags + ", value=" + value + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
Metric metric = (Metric) o;
|
||||
|
||||
if (name != null ? !name.equals(metric.name) : metric.name != null)
|
||||
return false;
|
||||
|
||||
if (tags != null) {
|
||||
if (tags.size() != metric.tags.size()) {
|
||||
return false;
|
||||
} else if (!tags.containsAll(metric.tags)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return value != null ? value.equals(metric.value) : metric.value == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name != null ? name.hashCode() : 0;
|
||||
result = 31 * result + (tags != null ? tags.hashCode() : 0);
|
||||
result = 31 * result + (value != null ? value.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple Builder for Metric. Note: cannot re-use a builder after build is
|
||||
* called.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
Metric metric;
|
||||
|
||||
private Builder() {
|
||||
metric = new Metric();
|
||||
}
|
||||
|
||||
private static TagParser parser = new TagParser();
|
||||
|
||||
public Builder tag(Tag tag) {
|
||||
metric.addTag(tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder tags(List<Tag> tags) {
|
||||
metric.setTags(tags);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder tag(String key, String value) {
|
||||
metric.addTag(new Tag(key, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder tag(String rawTag) {
|
||||
metric.addTag(parser.parse(rawTag));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
metric.setName(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder value(Value value) {
|
||||
metric.setValue(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder value(long timestamp, double measure) {
|
||||
metric.setValue(new Value(timestamp, measure));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Metric build() {
|
||||
return this.metric;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package timely.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonRootName;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Preconditions;
|
||||
import timely.model.parse.TagParser;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Tag consists of key value pair
|
||||
*/
|
||||
@JsonRootName("tag")
|
||||
public class Tag implements Comparable<Tag>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Joiner equalJoiner = Joiner.on("=");
|
||||
private static final TagParser tagParser = new TagParser();
|
||||
|
||||
private String key;
|
||||
private String value;
|
||||
|
||||
public Tag() {
|
||||
}
|
||||
|
||||
public Tag(Tag other) {
|
||||
this.setKey(other.getKey());
|
||||
this.setValue(other.getValue());
|
||||
}
|
||||
|
||||
public Tag(String tag) {
|
||||
this(tagParser.parse(tag));
|
||||
}
|
||||
|
||||
public Tag(String key, String value) {
|
||||
Preconditions.checkNotNull(key);
|
||||
Preconditions.checkNotNull(value);
|
||||
this.setKey(key);
|
||||
this.setValue(value);
|
||||
}
|
||||
|
||||
@JsonAnyGetter
|
||||
public Map<String, String> get() {
|
||||
return Stream.of(this).collect(Collectors.toMap(Tag::getKey, Tag::getValue));
|
||||
}
|
||||
|
||||
@JsonAnySetter
|
||||
public void set(String key, String value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins key and value into key=value
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String join() {
|
||||
return equalJoiner.join(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Tag{" + equalJoiner.join(this.getKey(), this.getValue()) + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.toString().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (null == o || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
return this.toString().equals(o.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Tag other) {
|
||||
return this.toString().compareTo(other.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package timely.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Value consists of a long timestamp and a double measurement
|
||||
*/
|
||||
public class Value implements Comparator<Value>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long timestamp;
|
||||
private Double measure;
|
||||
|
||||
public Value() {
|
||||
}
|
||||
|
||||
public Value(Value other) {
|
||||
this.setTimestamp(other.getTimestamp());
|
||||
this.setMeasure(other.getMeasure());
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public Value(@JsonProperty("timestamp") Long timestamp, @JsonProperty("measure") Double measure) {
|
||||
this.setTimestamp(timestamp);
|
||||
this.setMeasure(measure);
|
||||
}
|
||||
|
||||
@JsonProperty("timestamp")
|
||||
public Long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(final Long timestamp) {
|
||||
this.setTimestamp(timestamp, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void setTimestamp(final Long timestamp, TimeUnit timeUnit) {
|
||||
this.timestamp = timeUnit.toMillis(timestamp);
|
||||
}
|
||||
|
||||
@JsonProperty("measure")
|
||||
public Double getMeasure() {
|
||||
return measure;
|
||||
}
|
||||
|
||||
public void setMeasure(final Double measure) {
|
||||
this.measure = measure;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Compares timestamps for sorting.
|
||||
*
|
||||
* Note: this comparator imposes orderings that are inconsistent with equals
|
||||
*
|
||||
*/
|
||||
public int compare(Value v1, Value v2) {
|
||||
if (v1.getTimestamp().equals(v2.getTimestamp()))
|
||||
return 0;
|
||||
return v1.getTimestamp() > v2.getTimestamp() ? 1 : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (null == o || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
if (getTimestamp() != null ? !getTimestamp().equals(((Value) o).getTimestamp())
|
||||
: ((Value) o).getTimestamp() != null)
|
||||
return false;
|
||||
return !(getMeasure() != null ? !getMeasure().equals(((Value) o).getMeasure())
|
||||
: ((Value) o).getMeasure() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(timestamp, measure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Value{" + "timestamp=" + timestamp + ", measure=" + measure + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package timely.model.parse;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Splitter;
|
||||
import timely.model.Metric;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MetricParser implements Parser<Metric> {
|
||||
|
||||
private static final Splitter spaceSplitter = Splitter.on(" ").omitEmptyStrings().trimResults();
|
||||
|
||||
@Override
|
||||
public Metric parse(String line) {
|
||||
|
||||
// put specification
|
||||
//
|
||||
// put <metricName> <timestamp> <value> <tagK=tagV> <tagK=tagV> ...
|
||||
|
||||
List<String> parts = spaceSplitter.splitToList(line);
|
||||
Preconditions.checkPositionIndex(3, parts.size());
|
||||
|
||||
Metric.Builder builder = Metric.newBuilder();
|
||||
// index 0 is put
|
||||
builder.name(parts.get(1)); // metric name
|
||||
builder.value(Long.parseLong(parts.get(2)), Double.parseDouble(parts.get(3)));
|
||||
parts.stream().skip(4).forEach(builder::tag);
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package timely.model.parse;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Parser<T> {
|
||||
|
||||
T parse(String t);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package timely.model.parse;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import timely.model.Tag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parses tag1=val1,tag2=val2,tag3=val3 into a {@code List} of {@code Tag}
|
||||
*/
|
||||
public class TagListParser implements Parser<List<Tag>> {
|
||||
|
||||
private static final Splitter commaSplitter = Splitter.on(",").omitEmptyStrings();
|
||||
private static final TagParser tagParser = new TagParser();
|
||||
|
||||
@Override
|
||||
public List<Tag> parse(String t) {
|
||||
ArrayList<Tag> tags = new ArrayList<>();
|
||||
commaSplitter.splitToList(t).forEach(p -> tags.add(tagParser.parse(p)));
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package timely.model.parse;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import timely.model.Tag;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TagParser implements Parser<Tag> {
|
||||
|
||||
private static final Splitter equalSplitter = Splitter.on("=").trimResults();
|
||||
|
||||
@Override
|
||||
public Tag parse(String t) {
|
||||
List<String> parts = equalSplitter.splitToList(t);
|
||||
if (parts.size() != 2) {
|
||||
throw new IllegalArgumentException("Invalid tag format: " + t);
|
||||
}
|
||||
return new Tag(parts.get(0), parts.get(1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FindBugsFilter>
|
||||
</FindBugsFilter>
|
||||
@@ -0,0 +1,63 @@
|
||||
package timely.model;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class MetricTest {
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
Metric m1 = Metric.newBuilder().name("m1").tag("t1", "v1").tag("t2", "v2").value(1, 0.0).build();
|
||||
Metric m2 = Metric.newBuilder().name("m1").tag("t2", "v2").tag("t1", "v1").value(1, 0.0).build();
|
||||
|
||||
assertTrue(m1.equals(m2));
|
||||
assertTrue(m2.equals(m1));
|
||||
|
||||
Metric m3 = Metric.newBuilder().name("m1").tag("t1", "v1").value(1, 0.0).build();
|
||||
assertFalse(m1.equals(m3));
|
||||
|
||||
Metric m4 = Metric.newBuilder().name("m4").tag("t2", "v2").tag("t1", "v1").value(1, 0.0).build();
|
||||
assertFalse(m1.equals(m4));
|
||||
|
||||
Metric m5 = Metric.newBuilder().name("m1").tag("t3", "v3").tag("t2", "v2").tag("t1", "v1").value(1, 0.0)
|
||||
.build();
|
||||
assertFalse(m1.equals(m5));
|
||||
|
||||
Metric m6 = Metric.newBuilder().name("m1").tag("t2", "v2").tag("t1", "v1").value(2, 0.0).build();
|
||||
assertFalse(m1.equals(m6));
|
||||
|
||||
Metric m7 = Metric.newBuilder().name("m1").tag("t2", "v2").tag("t1", "v1").value(1, 1.0).build();
|
||||
assertFalse(m1.equals(m7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJson() throws IOException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
String expectedJson = "{\"name\":\"m1\",\"timestamp\":1,\"measure\":1.0,\"tags\":[{\"k1\":\"v1\"}]}";
|
||||
|
||||
Metric m1 = Metric.newBuilder().name("m1").tag("k1", "v1").value(1, 1.0).build();
|
||||
|
||||
Metric expectedMetric = mapper.readValue(expectedJson, Metric.class);
|
||||
|
||||
assertTrue(m1.equals(expectedMetric));
|
||||
|
||||
expectedJson = "{\"name\":\"m1\",\"tags\":[{\"k1\":\"v1\"}],\"timestamp\":5,\"measure\":5.0}";
|
||||
expectedMetric = mapper.readValue(expectedJson, Metric.class);
|
||||
|
||||
assertEquals((long) expectedMetric.getValue().getTimestamp(), 5L);
|
||||
assertEquals(expectedMetric.getValue().getMeasure(), 5.0D, 0.0);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
# Write NSQ plugin for CollectD
|
||||
|
||||
To build, run ```mvn clean package```. Place the resulting jar file somewhere on the local filesystem and add the following to the
|
||||
collectd configuration file
|
||||
|
||||
```
|
||||
LoadPlugin java
|
||||
<Plugin java>
|
||||
JVMArg "-verbose:jni"
|
||||
JVMArg "-Djava.class.path=/usr/share/collectd/java/collectd-api.jar:<path_to_jar>/collectd-nsq-plugin-0.0.1-SNAPSHOT.jar"
|
||||
JVMArg "-Xms1024m"
|
||||
JVMArg "-Xmx1024m"
|
||||
LoadPlugin "timely.collectd.plugin.WriteNSQPlugin"
|
||||
<Plugin "timely.collectd.plugin.WriteNSQPlugin">
|
||||
Host "<NSQHost[,NSQHost]>"
|
||||
Port "<NSQHttpPort>"
|
||||
</Plugin>
|
||||
</Plugin>
|
||||
```
|
||||
@@ -4,21 +4,17 @@
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<version>0.0.3</version>
|
||||
</parent>
|
||||
<artifactId>collectd-nsq-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>collectd</groupId>
|
||||
<artifactId>collectd-api</artifactId>
|
||||
<version>5.5.1</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${collectd-api-jar}</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
@@ -39,24 +35,10 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
<xmlOutput>true</xmlOutput>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<phase>prepare-package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.2</version>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
@@ -77,53 +59,7 @@
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
<version>0.5.2</version>
|
||||
<configuration>
|
||||
<configFile>${project.parent.basedir}/eclipse/Eclipse-Formatter.xml</configFile>
|
||||
<lineEnding>LF</lineEnding>
|
||||
<overrideConfigCompilerVersion>true</overrideConfigCompilerVersion>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
<artifactId>lifecycle-mapping</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<configuration>
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>
|
||||
formatter-maven-plugin
|
||||
</artifactId>
|
||||
<versionRange>
|
||||
[0.5.2,)
|
||||
</versionRange>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore/>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FindBugsFilter>
|
||||
</FindBugsFilter>
|
||||
@@ -1,14 +0,0 @@
|
||||
# CollectD Plugin
|
||||
|
||||
The CollectD plugin formats metrics acquired by the other plugins and sends them to Timely. The plugin contains code for parsing and extracting information from metrics reported by some of the plugins. One special case is the metrics received by the [StatsD] (https://github.com/etsy/statsd) plugin. It supports processing of [NSQ] (http://nsq.io/) metrics by looking for StatsD metrics with the prefix 'nsq'.
|
||||
|
||||
It also supports parsing of the StatsD metrics coming from the Hadoop Metrics2 framework (a metric with 4 components separated by periods). In this case you will want to set the skip hostname property to true and send the metrics from Hadoop to a local CollectD instance that will add the hostname and other tags.
|
||||
|
||||
The plugin allows for other unspecified formats to be sent as-is, except in the following case: if the StatsD metric contains a period, everything up to the period will be used in an instance tag.
|
||||
|
||||
For example:
|
||||
|
||||
"foo:1|c" will end up as the metric 'statsd.foo'
|
||||
|
||||
"bar.foo:1|c" will end up as the metric 'statsd.foo' with an 'instance=bar' tag.
|
||||
|
||||
@@ -4,21 +4,17 @@
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<version>0.0.3</version>
|
||||
</parent>
|
||||
<artifactId>collectd-plugin-parent</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>collectd</groupId>
|
||||
<artifactId>collectd-api</artifactId>
|
||||
<version>5.5.1</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${collectd-api-jar}</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.jcip</groupId>
|
||||
<artifactId>jcip-annotations</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -29,7 +25,6 @@
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-easymock-release-full</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<classifier>full</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
@@ -47,79 +42,19 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
<xmlOutput>true</xmlOutput>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<phase>prepare-package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.19</version>
|
||||
<configuration>
|
||||
<excludedGroups>timely.test.IntegrationTest</excludedGroups>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
<version>0.5.2</version>
|
||||
<configuration>
|
||||
<configFile>${project.parent.basedir}/eclipse/Eclipse-Formatter.xml</configFile>
|
||||
<lineEnding>LF</lineEnding>
|
||||
<overrideConfigCompilerVersion>true</overrideConfigCompilerVersion>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
<artifactId>lifecycle-mapping</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<configuration>
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>
|
||||
formatter-maven-plugin
|
||||
</artifactId>
|
||||
<versionRange>
|
||||
[0.5.2,)
|
||||
</versionRange>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore/>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
@@ -64,7 +64,9 @@ public abstract class CollectDPluginParent {
|
||||
String host = vl.getHost();
|
||||
int idx = host.indexOf(PERIOD);
|
||||
if (-1 != idx) {
|
||||
tags.append(" host=").append(vl.getHost().substring(0, idx));
|
||||
tags.append(" host=").append(host.substring(0, idx));
|
||||
} else {
|
||||
tags.append(" host=").append(host);
|
||||
}
|
||||
int nIdx = host.indexOf('n');
|
||||
if (-1 != nIdx) {
|
||||
@@ -167,6 +169,9 @@ public abstract class CollectDPluginParent {
|
||||
} else if (vl.getPlugin().equals("ipmi")) {
|
||||
metric.append("sys.ipmi.").append(vl.getType());
|
||||
tags.append(INSTANCE).append(vl.getTypeInstance().replaceAll(" ", "_"));
|
||||
} else if (vl.getPlugin().equals("snmp")) {
|
||||
metric.append("sys.snmp.").append(vl.getType());
|
||||
tags.append(INSTANCE).append(vl.getTypeInstance().replaceAll(" ", "_"));
|
||||
} else if (notEmpty(vl.getTypeInstance()) && notEmpty(vl.getType()) && notEmpty(vl.getPluginInstance())
|
||||
&& notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType()).append(PERIOD)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FindBugsFilter>
|
||||
</FindBugsFilter>
|
||||
@@ -185,6 +185,28 @@ public class PluginParentTest {
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnmp() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("snmp");
|
||||
// vl.setPluginInstance("sda");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("if_octets");
|
||||
vl.setTypeInstance("Ethernet1");
|
||||
vl.setValues(Collections.singletonList((Number) 0.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
assertEquals(
|
||||
"put sys.snmp.if_octets 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo instance=Ethernet1 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSensors() {
|
||||
ValueList vl = new ValueList();
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
# Write Timely plugin for CollectD
|
||||
|
||||
To build, run ```mvn clean package```. Place the resulting jar file somewhere on the local filesystem and add the following to the
|
||||
collectd configuration file
|
||||
|
||||
```
|
||||
LoadPlugin java
|
||||
<Plugin java>
|
||||
JVMArg "-verbose:jni"
|
||||
JVMArg "-Djava.class.path=/usr/share/collectd/java/collectd-api.jar:<path_to_jar>/collectd-timely-plugin-0.0.1-SNAPSHOT.jar"
|
||||
JVMArg "-Xms1024m"
|
||||
JVMArg "-Xmx1024m"
|
||||
LoadPlugin "timely.collectd.plugin.WriteTimelyPlugin"
|
||||
<Plugin "timely.collectd.plugin.WriteTimelyPlugin">
|
||||
Host "<TimelyHost>"
|
||||
Port "<TimelyPutPort>"
|
||||
</Plugin>
|
||||
</Plugin>
|
||||
```
|
||||
@@ -4,21 +4,17 @@
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<version>0.0.3</version>
|
||||
</parent>
|
||||
<artifactId>collectd-timely-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>collectd</groupId>
|
||||
<artifactId>collectd-api</artifactId>
|
||||
<version>5.5.1</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${collectd-api-jar}</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.jcip</groupId>
|
||||
<artifactId>jcip-annotations</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -34,7 +30,6 @@
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-easymock-release-full</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<classifier>full</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
@@ -57,25 +52,10 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
<xmlOutput>true</xmlOutput>
|
||||
<excludeFilterFile>src/main/resources/findbugs-excludes.xml</excludeFilterFile>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<phase>prepare-package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.2</version>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
@@ -96,62 +76,15 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.19</version>
|
||||
<configuration>
|
||||
<excludedGroups>timely.test.IntegrationTest</excludedGroups>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
<version>0.5.2</version>
|
||||
<configuration>
|
||||
<configFile>${project.parent.basedir}/eclipse/Eclipse-Formatter.xml</configFile>
|
||||
<lineEnding>LF</lineEnding>
|
||||
<overrideConfigCompilerVersion>true</overrideConfigCompilerVersion>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
<artifactId>lifecycle-mapping</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<configuration>
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>
|
||||
formatter-maven-plugin
|
||||
</artifactId>
|
||||
<versionRange>
|
||||
[0.5.2,)
|
||||
</versionRange>
|
||||
<goals>
|
||||
<goal>format</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore/>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
@@ -52,7 +52,7 @@ public class WriteTimelyPlugin extends CollectDPluginParent implements CollectdC
|
||||
}
|
||||
} else {
|
||||
Collectd.logWarning("Not writing to Timely, waiting to reconnect");
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<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>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.3</version>
|
||||
</parent>
|
||||
<artifactId>timely-common</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,15 @@
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
||||
<id>dist</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>timely-app</directory>
|
||||
<outputDirectory>timely-app</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
</assembly>
|
||||
@@ -0,0 +1,32 @@
|
||||
<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>
|
||||
<artifactId>timely-app</artifactId>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.3</version>
|
||||
</parent>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>assemble</id>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
<tarLongFileMode>posix</tarLongFileMode>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
module.exports = function(grunt) {
|
||||
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.loadNpmTasks('grunt-execute');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
|
||||
grunt.initConfig({
|
||||
|
||||
clean: ["dist"],
|
||||
|
||||
copy: {
|
||||
src_to_dist: {
|
||||
cwd: 'src',
|
||||
expand: true,
|
||||
src: ['**/*', '!**/*.js', '!**/*.scss'],
|
||||
dest: 'dist'
|
||||
},
|
||||
img_to_dist: {
|
||||
cwd: 'src',
|
||||
expand: true,
|
||||
src: ['img/*'],
|
||||
dest: 'dist/src/'
|
||||
},
|
||||
pluginDef: {
|
||||
expand: true,
|
||||
src: ['plugin.json', 'README.md'],
|
||||
dest: 'dist',
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
rebuild_all: {
|
||||
files: ['src/**/*', 'plugin.json', 'README.md'],
|
||||
tasks: ['default'],
|
||||
options: {spawn: false}
|
||||
},
|
||||
},
|
||||
|
||||
babel: {
|
||||
options: {
|
||||
sourceMap: true,
|
||||
presets: ["es2015"],
|
||||
plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"],
|
||||
},
|
||||
dist: {
|
||||
files: [{
|
||||
cwd: 'src',
|
||||
expand: true,
|
||||
src: ['**/*.js'],
|
||||
dest: 'dist',
|
||||
ext:'.js'
|
||||
}]
|
||||
},
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
grunt.registerTask('default',
|
||||
['clean',
|
||||
'copy:src_to_dist',
|
||||
'copy:pluginDef',
|
||||
'copy:img_to_dist',
|
||||
'babel'
|
||||
]);
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
# Timely
|
||||
|
||||
Timely: a scalable, distributed, **secure** time series database.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Timely
|
||||
|
||||
Timely: a scalable, distributed, **secure** time series database.
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-13">OpenTSDB metrics query</span>
|
||||
<input type="text" class="gf-form-input" ng-model='ctrl.annotation.target' placeholder="events.eventname"></input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-13">Show Global Annotations?</span>
|
||||
<editor-checkbox text="" model="ctrl.annotation.isGlobal"></editor-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Timely Login</h1>
|
||||
</div>
|
||||
|
||||
<div ng-if="ctrl.datasources.length === 0">
|
||||
<h4>No Timely datasources are defined.</h4>
|
||||
<i><a href="datasources/new">Create one</a></i>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="source in ctrl.datasources" ng-init="d=source" ng-controller="AuthsCtrl as actrl">
|
||||
<div class="gf-box">
|
||||
<div class="gf-box-header p-x-1 p-y-1">
|
||||
<h3 class="">{{d.name}}
|
||||
<span class="pull-right">
|
||||
<h6><i>https://{{d.jsonData.timelyHost}}:{{d.jsonData.httpsPort}}/login</i></h6>
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="gf-box-body">
|
||||
<div ng-if="d.jsonData.basicAuths" class="p-b-1">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-7">
|
||||
User
|
||||
</span>
|
||||
<input id="timlely-user" class="gf-form-input max-width-21" type="text" ng-model='actrl.basicAuthUser' placeholder="user" required></input>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-7">
|
||||
Password
|
||||
</span>
|
||||
<input id="timely-password" class="gf-form-input max-width-21" type="password" ng-model='actrl.basicAuthPassword' placeholder="password" required></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="actrl.fetching" class="gf-row">
|
||||
<div class="alert-{{actrl.fetching.status}} alert">
|
||||
<div class="alert-title">{{actrl.fetching.title}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-button-row center-block">
|
||||
<button type="submit" class="btn btn-success" ng-click="actrl.login(d)">
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,146 @@
|
||||
'use strict';
|
||||
|
||||
System.register(['lodash'], function (_export, _context) {
|
||||
"use strict";
|
||||
|
||||
var _, _createClass, AuthsCtrl;
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [function (_lodash) {
|
||||
_ = _lodash.default;
|
||||
}],
|
||||
execute: function () {
|
||||
_createClass = function () {
|
||||
function defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return function (Constructor, protoProps, staticProps) {
|
||||
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
};
|
||||
}();
|
||||
|
||||
_export('AuthsCtrl', AuthsCtrl = function () {
|
||||
function AuthsCtrl(backendSrv, $rootScope, $timeout) {
|
||||
_classCallCheck(this, AuthsCtrl);
|
||||
|
||||
this.$rootScope = $rootScope;
|
||||
this.$timeout = $timeout;
|
||||
this.backendSrv = backendSrv;
|
||||
}
|
||||
|
||||
_createClass(AuthsCtrl, [{
|
||||
key: 'login',
|
||||
value: function login(datasource) {
|
||||
this.fetching = {};
|
||||
this.fetching.done = false;
|
||||
this.fetching.show = true;
|
||||
this.fetching.status = 'warning';
|
||||
this.fetching.title = 'Fetching auths...';
|
||||
|
||||
//this.$rootScope.appEvent('alert-success', ['Authorization Cookie Received', '']);
|
||||
//this.$rootScope.appEvent('alert-error', ['Authorization Failed', '']);
|
||||
//this.$rootScope.appEvent('alert-warning', ['Fetching Authorizations', '']);
|
||||
|
||||
var uri = this._getURI(datasource.jsonData);
|
||||
|
||||
if (datasource.jsonData.basicAuths) {
|
||||
if (this.basicAuthUser && this.basicAuthPassword) {
|
||||
var authBlob = { username: this.basicAuthUser, password: this.basicAuthPassword };
|
||||
this.doPostLogin(uri, authBlob);
|
||||
} else {
|
||||
this.$rootScope.appEvent('alert-warning', [datasource.name, 'requires User/Password']);
|
||||
this.fetching.status = 'warning';
|
||||
this.fetching.title = 'User/Password required';
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.doGetLogin(uri);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'doPostLogin',
|
||||
value: function doPostLogin(uri, authBlob) {
|
||||
this.doXHR('POST', uri, authBlob);
|
||||
}
|
||||
}, {
|
||||
key: 'doGetLogin',
|
||||
value: function doGetLogin(uri) {
|
||||
this.doXHR('GET', uri);
|
||||
}
|
||||
}, {
|
||||
key: 'doXHR',
|
||||
value: function doXHR(verb, uri, data) {
|
||||
if (!data) {
|
||||
data = "";
|
||||
}
|
||||
var r = new XMLHttpRequest();
|
||||
r.open(verb, uri, true);
|
||||
r.withCredentials = true;
|
||||
r.setRequestHeader('Content-Type', 'application/json');
|
||||
r.onreadystatechange = function () {
|
||||
this.handleLoginResult(r);
|
||||
}.bind(this);
|
||||
r.send(JSON.stringify(data));
|
||||
}
|
||||
}, {
|
||||
key: 'handleLoginResult',
|
||||
value: function handleLoginResult(response) {
|
||||
// wait for readyState to be '4 => done'
|
||||
if (response.readyState != 4) return;
|
||||
|
||||
this.fetching.done = true;
|
||||
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
var cookie = (document.cookie.match(/^(?:.*;)?TSESSIONID=([^;]+)(?:.*)?$/) || [, null])[1];
|
||||
console.log(cookie);
|
||||
console.log(document.cookie);
|
||||
|
||||
this.fetching.status = 'success';
|
||||
this.fetching.title = 'Login successful';
|
||||
this.$rootScope.appEvent('alert-success', ['Success', '']);
|
||||
break;
|
||||
default:
|
||||
this.fetching.status = 'warning';
|
||||
this.fetching.title = response.statusText;
|
||||
this.$rootScope.appEvent('alert-warning', [response.status + ': ' + response.statusText, '']);
|
||||
break;
|
||||
}
|
||||
|
||||
if (r.readyState != 4 || r.status != 200) return;
|
||||
alert("Success: " + r.getResponseHeader('Set-Cookie'));
|
||||
}
|
||||
}, {
|
||||
key: '_getURI',
|
||||
value: function _getURI(jsonData) {
|
||||
return 'https://' + jsonData.timelyHost + ':' + jsonData.httpsPort + '/login';
|
||||
}
|
||||
}, {
|
||||
key: '_showResults',
|
||||
value: function _showResults() {}
|
||||
}]);
|
||||
|
||||
return AuthsCtrl;
|
||||
}());
|
||||
|
||||
_export('AuthsCtrl', AuthsCtrl);
|
||||
}
|
||||
};
|
||||
});
|
||||
//# sourceMappingURL=auths.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/components/auths.js"],"names":["_","AuthsCtrl","backendSrv","$rootScope","$timeout","datasource","fetching","done","show","status","title","uri","_getURI","jsonData","basicAuths","basicAuthUser","basicAuthPassword","authBlob","username","password","doPostLogin","appEvent","name","doGetLogin","doXHR","verb","data","r","XMLHttpRequest","open","withCredentials","setRequestHeader","onreadystatechange","handleLoginResult","bind","send","JSON","stringify","response","readyState","cookie","document","match","console","log","statusText","alert","getResponseHeader","timelyHost","httpsPort"],"mappings":"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","file":"auths.js","sourcesContent":["import _ from 'lodash';\n\nexport class AuthsCtrl {\n\n constructor(backendSrv, $rootScope, $timeout){\n this.$rootScope = $rootScope;\n this.$timeout = $timeout;\n this.backendSrv = backendSrv;\n }\n\n login(datasource){\n this.fetching = {};\n this.fetching.done = false;\n this.fetching.show = true;\n this.fetching.status = 'warning';\n this.fetching.title = 'Fetching auths...';\n\n //this.$rootScope.appEvent('alert-success', ['Authorization Cookie Received', '']);\n //this.$rootScope.appEvent('alert-error', ['Authorization Failed', '']);\n //this.$rootScope.appEvent('alert-warning', ['Fetching Authorizations', '']);\n\n var uri = this._getURI(datasource.jsonData);\n\n if(datasource.jsonData.basicAuths){\n if( this.basicAuthUser && this.basicAuthPassword){\n var authBlob = {username: this.basicAuthUser, password: this.basicAuthPassword};\n this.doPostLogin(uri, authBlob);\n } else {\n this.$rootScope.appEvent('alert-warning', [datasource.name, 'requires User/Password']);\n this.fetching.status = 'warning';\n this.fetching.title = 'User/Password required';\n\n return;\n }\n } else {\n this.doGetLogin(uri);\n }\n\n }\n\n doPostLogin(uri, authBlob){\n this.doXHR('POST', uri, authBlob);\n }\n\n doGetLogin(uri){\n this.doXHR('GET', uri)\n }\n\n doXHR(verb, uri, data){\n if( !data){\n data = \"\";\n }\n var r = new XMLHttpRequest();\n r.open(verb, uri, true);\n r.withCredentials = true;\n r.setRequestHeader('Content-Type', 'application/json');\n r.onreadystatechange = function () {\n this.handleLoginResult(r);\n }.bind(this);\n r.send(JSON.stringify(data));\n }\n\n handleLoginResult(response){\n // wait for readyState to be '4 => done'\n if (response.readyState != 4) return;\n\n this.fetching.done = true;\n\n switcLine truncated
|
||||
@@ -0,0 +1,80 @@
|
||||
'use strict';
|
||||
|
||||
System.register(['lodash', 'app/core/core', 'plugins/timely-app/components/auths'], function (_export, _context) {
|
||||
"use strict";
|
||||
|
||||
var _, coreModule, appEvents, AuthsCtrl, _createClass, AuthsPageCtrl;
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [function (_lodash) {
|
||||
_ = _lodash.default;
|
||||
}, function (_appCoreCore) {
|
||||
coreModule = _appCoreCore.coreModule;
|
||||
appEvents = _appCoreCore.appEvents;
|
||||
}, function (_pluginsTimelyAppComponentsAuths) {
|
||||
AuthsCtrl = _pluginsTimelyAppComponentsAuths.AuthsCtrl;
|
||||
}],
|
||||
execute: function () {
|
||||
_createClass = function () {
|
||||
function defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return function (Constructor, protoProps, staticProps) {
|
||||
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
};
|
||||
}();
|
||||
|
||||
_export('AuthsPageCtrl', AuthsPageCtrl = function () {
|
||||
function AuthsPageCtrl(backendSrv, datasourceSrv) {
|
||||
_classCallCheck(this, AuthsPageCtrl);
|
||||
|
||||
this.datasourceSrv = datasourceSrv;
|
||||
this.datasources = this.getTimelyDatasources();
|
||||
coreModule.controller('AuthsCtrl', AuthsCtrl);
|
||||
}
|
||||
|
||||
_createClass(AuthsPageCtrl, [{
|
||||
key: 'getTimelyDatasources',
|
||||
value: function getTimelyDatasources() {
|
||||
var allSources = this.datasourceSrv.getAll();
|
||||
|
||||
var sources = _.map(allSources, function (value, key, col) {
|
||||
var ret = {};
|
||||
ret.meta = {};
|
||||
ret.meta.type = col[key].meta.type;
|
||||
ret.meta.id = col[key].meta.id;
|
||||
ret.name = key;
|
||||
ret.jsonData = col[key].jsonData;
|
||||
return ret;
|
||||
});
|
||||
return _.filter(sources, function (d) {
|
||||
return d.meta.type === 'datasource' && d.meta.id === 'timely-datasource';
|
||||
});
|
||||
}
|
||||
}]);
|
||||
|
||||
return AuthsPageCtrl;
|
||||
}());
|
||||
|
||||
_export('AuthsPageCtrl', AuthsPageCtrl);
|
||||
|
||||
AuthsPageCtrl.templateUrl = 'components/auths.html';
|
||||
}
|
||||
};
|
||||
});
|
||||
//# sourceMappingURL=authspage.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/components/authspage.js"],"names":["_","coreModule","appEvents","AuthsCtrl","AuthsPageCtrl","backendSrv","datasourceSrv","datasources","getTimelyDatasources","controller","allSources","getAll","sources","map","value","key","col","ret","meta","type","id","name","jsonData","filter","d","templateUrl"],"mappings":";;;;;;;;;;;;;;;AACOA,O;;AACCC,gB,gBAAAA,U;AAAYC,e,gBAAAA,S;;AACZC,e,oCAAAA,S;;;;;;;;;;;;;;;;;;;;;+BAEKC,a;AAEX,+BAAYC,UAAZ,EAAwBC,aAAxB,EAAsC;AAAA;;AACpC,eAAKA,aAAL,GAAqBA,aAArB;AACA,eAAKC,WAAL,GAAmB,KAAKC,oBAAL,EAAnB;AACAP,qBAAWQ,UAAX,CAAsB,WAAtB,EAAmCN,SAAnC;AACD;;;;iDAEqB;AACpB,gBAAIO,aAAa,KAAKJ,aAAL,CAAmBK,MAAnB,EAAjB;;AAEA,gBAAIC,UAAUZ,EAAEa,GAAF,CAAMH,UAAN,EAAkB,UAASI,KAAT,EAAgBC,GAAhB,EAAqBC,GAArB,EAAyB;AACvD,kBAAIC,MAAM,EAAV;AACAA,kBAAIC,IAAJ,GAAW,EAAX;AACAD,kBAAIC,IAAJ,CAASC,IAAT,GAAgBH,IAAID,GAAJ,EAASG,IAAT,CAAcC,IAA9B;AACAF,kBAAIC,IAAJ,CAASE,EAAT,GAAcJ,IAAID,GAAJ,EAASG,IAAT,CAAcE,EAA5B;AACAH,kBAAII,IAAJ,GAAWN,GAAX;AACAE,kBAAIK,QAAJ,GAAeN,IAAID,GAAJ,EAASO,QAAxB;AACA,qBAAOL,GAAP;AACD,aARa,CAAd;AASA,mBAAOjB,EAAEuB,MAAF,CAASX,OAAT,EAAkB,UAASY,CAAT,EAAW;AAClC,qBAAQA,EAAEN,IAAF,CAAOC,IAAP,KAAgB,YAAhB,IAAgCK,EAAEN,IAAF,CAAOE,EAAP,KAAc,mBAAtD;AACD,aAFM,CAAP;AAGD;;;;;;;;AAGHhB,oBAAcqB,WAAd,GAA4B,uBAA5B","file":"authspage.js","sourcesContent":["\nimport _ from 'lodash';\nimport {coreModule, appEvents} from 'app/core/core';\nimport {AuthsCtrl} from 'plugins/timely-app/components/auths';\n\nexport class AuthsPageCtrl {\n\n constructor(backendSrv, datasourceSrv){\n this.datasourceSrv = datasourceSrv;\n this.datasources = this.getTimelyDatasources();\n coreModule.controller('AuthsCtrl', AuthsCtrl);\n }\n\n getTimelyDatasources(){\n var allSources = this.datasourceSrv.getAll();\n\n var sources = _.map(allSources, function(value, key, col){\n var ret = {};\n ret.meta = {};\n ret.meta.type = col[key].meta.type;\n ret.meta.id = col[key].meta.id;\n ret.name = key;\n ret.jsonData = col[key].jsonData;\n return ret;\n });\n return _.filter(sources, function(d){\n return (d.meta.type === 'datasource' && d.meta.id === 'timely-datasource');\n });\n }\n\n}\nAuthsPageCtrl.templateUrl = 'components/auths.html';\n"]}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
<h3 class="page-heading">Timely App Configuration</h3>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">Currently, all configuration is done in the Timely datasource.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
System.register([], function (_export, _context) {
|
||||
"use strict";
|
||||
|
||||
var TimelyAppConfigCtrl;
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [],
|
||||
execute: function () {
|
||||
_export('TimelyAppConfigCtrl', TimelyAppConfigCtrl = function TimelyAppConfigCtrl() {
|
||||
_classCallCheck(this, TimelyAppConfigCtrl);
|
||||
});
|
||||
|
||||
_export('TimelyAppConfigCtrl', TimelyAppConfigCtrl);
|
||||
|
||||
TimelyAppConfigCtrl.templateUrl = 'components/config.html';
|
||||
}
|
||||
};
|
||||
});
|
||||
//# sourceMappingURL=config.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/components/config.js"],"names":["TimelyAppConfigCtrl","templateUrl"],"mappings":";;;;;;;;;;;;;;;;qCACaA,mB,GAEX,+BAAc;AAAA;AAEb,O;;;;AAGHA,0BAAoBC,WAApB,GAAkC,wBAAlC","file":"config.js","sourcesContent":["\nexport class TimelyAppConfigCtrl {\n\n constructor() {\n \n }\n\n}\nTimelyAppConfigCtrl.templateUrl = 'components/config.html';\n"]}
|
||||
@@ -0,0 +1,53 @@
|
||||
<div class="page-header">
|
||||
<h1>Metrics Browser</h1>
|
||||
</div>
|
||||
|
||||
<div ng-hide="ctrl.hasDatasource" >
|
||||
<h4>No Timely datasources are defined.</h4>
|
||||
<i><a href="datasources/new">Create one</a></i>
|
||||
</div>
|
||||
|
||||
<div class="gf-box" ng-show="ctrl.hasDatasource">
|
||||
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-label width-7 p-y-1 p-x-1">Datasource</span>
|
||||
<div class="gf-form-select-wrapper gf-form-select-wrapper--has-help-icon max-width-24">
|
||||
<select class="gf-form-input"
|
||||
ng-model="ctrl.source"
|
||||
ng-options="d.name for d in ctrl.datasources"
|
||||
ng-change=ctrl.updateDatasource()></select>
|
||||
<info-popover mode="right-absolute">
|
||||
Select a Datasource<br>
|
||||
</info-popover>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-box-body">
|
||||
<div class="grafana-options-table">
|
||||
<div class="gf-form width-20 pull-right">
|
||||
<input type="text" class="gf-form-input" placeholder="filter" ng-model="searchText">
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Metric</th>
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
<tr ng-repeat="metric in ctrl.metrics | filter:searchText">
|
||||
<td>{{metric.metric}}</td>
|
||||
<td class="p-x-1" ng-repeat="tag in metric.tags">
|
||||
{{tag.name}} = {{tag.values}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,124 @@
|
||||
'use strict';
|
||||
|
||||
System.register(['lodash', 'plugins/timely-app/components/authspage'], function (_export, _context) {
|
||||
"use strict";
|
||||
|
||||
var _, AuthsPageCtrl, _createClass, MetricsPageCtrl;
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [function (_lodash) {
|
||||
_ = _lodash.default;
|
||||
}, function (_pluginsTimelyAppComponentsAuthspage) {
|
||||
AuthsPageCtrl = _pluginsTimelyAppComponentsAuthspage.AuthsPageCtrl;
|
||||
}],
|
||||
execute: function () {
|
||||
_createClass = function () {
|
||||
function defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return function (Constructor, protoProps, staticProps) {
|
||||
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
};
|
||||
}();
|
||||
|
||||
_export('MetricsPageCtrl', MetricsPageCtrl = function () {
|
||||
function MetricsPageCtrl(backendSrv, datasourceSrv) {
|
||||
_classCallCheck(this, MetricsPageCtrl);
|
||||
|
||||
this.hasDatasource = false;
|
||||
this.backendSrv = backendSrv;
|
||||
var apc = new AuthsPageCtrl(backendSrv, datasourceSrv);
|
||||
this.datasources = apc.getTimelyDatasources();
|
||||
if (this.datasources.length > 0) {
|
||||
this.source = this.datasources[0];
|
||||
this.updateDatasource();
|
||||
}
|
||||
}
|
||||
|
||||
_createClass(MetricsPageCtrl, [{
|
||||
key: 'updateDatasource',
|
||||
value: function updateDatasource() {
|
||||
var _this = this;
|
||||
|
||||
this._clearMetrics();
|
||||
var uri = this._getURI(this.source.jsonData);
|
||||
this.backendSrv.request({ method: 'GET',
|
||||
url: uri,
|
||||
withCredentials: true,
|
||||
params: "" }).then(function (response) {
|
||||
_this.metrics = _this._transformMetrics(response.metrics);
|
||||
_this.hasDatasource = true;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: '_clearMetrics',
|
||||
value: function _clearMetrics() {
|
||||
this.metrics = [];
|
||||
}
|
||||
}, {
|
||||
key: '_transformMetrics',
|
||||
value: function _transformMetrics(metrics) {
|
||||
var ret = [];
|
||||
_.each(metrics, function (value, idx) {
|
||||
var metric = {};
|
||||
metric.metric = value.metric;
|
||||
metric.tags = [];
|
||||
_.map(value.tags, function (tag, key) {
|
||||
// result || (result = {});
|
||||
// result['name'] = tag.key;
|
||||
// result['values'] || (result['values'] = []);
|
||||
// result['values'].push(tag.value);
|
||||
var gIdx = _.findIndex(metric.tags, { 'name': tag.key });
|
||||
var grouped;
|
||||
if (gIdx == -1) {
|
||||
grouped = { name: '', values: [] };
|
||||
} else {
|
||||
grouped = metric.tags[gIdx];
|
||||
}
|
||||
grouped.values.push(tag.value);
|
||||
grouped.values = _.sortBy(_.uniq(grouped.values));
|
||||
grouped.name = tag.key;
|
||||
if (gIdx > -1) {
|
||||
metric.tags[gIdx] = grouped;
|
||||
} else {
|
||||
metric.tags.push(grouped);
|
||||
}
|
||||
}.bind(metric));
|
||||
metric.tags = _.sortBy(metric.tags, 'name');
|
||||
ret.push(metric);
|
||||
}.bind(ret));
|
||||
|
||||
return _.sortBy(ret, 'metric');
|
||||
}
|
||||
}, {
|
||||
key: '_getURI',
|
||||
value: function _getURI(jsonData) {
|
||||
return 'https://' + jsonData.timelyHost + ':' + jsonData.httpsPort + '/api/metrics';
|
||||
}
|
||||
}]);
|
||||
|
||||
return MetricsPageCtrl;
|
||||
}());
|
||||
|
||||
_export('MetricsPageCtrl', MetricsPageCtrl);
|
||||
|
||||
MetricsPageCtrl.templateUrl = 'components/metrics.html';
|
||||
}
|
||||
};
|
||||
});
|
||||
//# sourceMappingURL=metrics.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/components/metrics.js"],"names":["_","AuthsPageCtrl","MetricsPageCtrl","backendSrv","datasourceSrv","hasDatasource","apc","datasources","getTimelyDatasources","length","source","updateDatasource","_clearMetrics","uri","_getURI","jsonData","request","method","url","withCredentials","params","then","metrics","_transformMetrics","response","ret","each","value","idx","metric","tags","map","tag","key","gIdx","findIndex","grouped","name","values","push","sortBy","uniq","bind","timelyHost","httpsPort","templateUrl"],"mappings":"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","file":"metrics.js","sourcesContent":["\nimport _ from 'lodash';\nimport {AuthsPageCtrl} from 'plugins/timely-app/components/authspage';\n\nexport class MetricsPageCtrl {\n\n constructor(backendSrv, datasourceSrv){\n this.hasDatasource = false;\n this.backendSrv = backendSrv;\n var apc = new AuthsPageCtrl(backendSrv, datasourceSrv);\n this.datasources = apc.getTimelyDatasources();\n if( this.datasources.length > 0){\n this.source = this.datasources[0];\n this.updateDatasource();\n }\n }\n\n updateDatasource(){\n this._clearMetrics();\n var uri = this._getURI(this.source.jsonData);\n this.backendSrv.request(\n { method: 'GET',\n url: uri,\n withCredentials: true,\n params: \"\" }\n ).then(\n response => {\n this.metrics = this._transformMetrics(response.metrics);\n this.hasDatasource = true;\n });\n }\n\n _clearMetrics(){\n this.metrics = [];\n }\n\n _transformMetrics(metrics){\n var ret = [];\n _.each(metrics, function(value, idx){\n var metric = {};\n metric.metric = value.metric;\n metric.tags = [];\n _.map(value.tags, function(tag, key){\n// result || (result = {});\n// result['name'] = tag.key;\n// result['values'] || (result['values'] = []);\n// result['values'].push(tag.value);\n var gIdx = _.findIndex(metric.tags, {'name': tag.key});\n var grouped;\n if( gIdx == -1 ){\n grouped = {name: '', values: []};\n } else {\n grouped = metric.tags[gIdx];\n }\n grouped.values.push(tag.value);\n grouped.values = _.sortBy(_.uniq(grouped.values))\n grouped.name = tag.key;\n if( gIdx > -1 ){\n metric.tags[gIdx] = grouped;\n } else {\n metric.tags.push(grouped);\n }\n }.bind(metric));\n metric.tags = _.sortBy(metric.tags, 'name')\n ret.push(metric);\n }.bind(ret));\n\n return _.sortBy(ret, 'metric');\n }\n\n _getURI(jsonData){\n return 'https://'+jsonData.timelyHost+':'+jsonData.httpsPort+'/api/metrics'\n }\n\n}\nMetricsPageCtrl.templateUrl = 'components/metrics.html';\n"]}
|
||||
@@ -0,0 +1,257 @@
|
||||
<query-editor-row query-ctrl="ctrl" can-collapse="false">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-25">
|
||||
<label class="gf-form-label query-keyword width-8">
|
||||
Metric
|
||||
<label class="gf-form-label" bs-tooltip="ctrl.errors.metric" style="color: rgb(229, 189, 28)" ng-show="ctrl.errors.metric">
|
||||
<i class="fa fa-warning"></i>
|
||||
</label>
|
||||
</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.target.metric"
|
||||
spellcheck='false' bs-typeahead="ctrl.suggestMetrics" placeholder="metric name" data-min-length=0 data-items=100
|
||||
ng-blur="ctrl.targetBlur()">
|
||||
</input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword">
|
||||
Aggregator
|
||||
<a bs-tooltip="ctrl.errors.aggregator" style="color: rgb(229, 189, 28)" ng-show="ctrl.errors.aggregator">
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</label>
|
||||
<div class="gf-form-select-wrapper max-width-15">
|
||||
<select ng-model="ctrl.target.aggregator" class="gf-form-input"
|
||||
ng-options="agg for agg in ctrl.aggregators"
|
||||
ng-change="ctrl.targetBlur()">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form max-width-20">
|
||||
<label class="gf-form-label query-keyword width-6">
|
||||
Alias:
|
||||
<info-popover mode="right-normal">
|
||||
Use patterns like $tag_tagname to replace part of the alias for a tag value
|
||||
</info-popover>
|
||||
</label>
|
||||
<input type="text" class="gf-form-input"
|
||||
ng-model="ctrl.target.alias"
|
||||
spellcheck='false'
|
||||
placeholder="series alias"
|
||||
data-min-length=0 data-items=100
|
||||
ng-blur="ctrl.targetBlur()"></input>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-25">
|
||||
<label class="gf-form-label query-keyword width-8">Down sample</label>
|
||||
<input type="text" class="gf-form-input"
|
||||
ng-model="ctrl.target.downsampleInterval"
|
||||
ng-model-onblur
|
||||
ng-change="ctrl.targetBlur()"
|
||||
placeholder="interval"></input>
|
||||
<info-popover mode="right-absolute">
|
||||
blank for auto, or for example <code>1m</code>
|
||||
</info-popover>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword">Aggregator</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select ng-model="ctrl.target.downsampleAggregator" class="gf-form-input"
|
||||
ng-options="agg for agg in ctrl.aggregators"
|
||||
ng-change="ctrl.targetBlur()">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-if="ctrl.tsdbVersion >= 2">
|
||||
<label class="gf-form-label query-keyword width-6">Fill</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select ng-model="ctrl.target.downsampleFillPolicy" class="gf-form-input"
|
||||
ng-options="agg for agg in ctrl.fillPolicies"
|
||||
ng-change="ctrl.targetBlur()">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<gf-form-switch class="gf-form"
|
||||
label="Disable downsampling"
|
||||
label-class="query-keyword"
|
||||
checked="ctrl.target.disableDownsampling"
|
||||
on-change="ctrl.targetBlur()">
|
||||
</gf-form-switch>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline" ng-if="ctrl.tsdbVersion >= 2">
|
||||
<div class="gf-form">
|
||||
|
||||
<label class="gf-form-label query-keyword width-8">
|
||||
Filters
|
||||
<info-popover mode="right-normal">
|
||||
Filters does not work with tags, either of the two will work but not both.
|
||||
</info-popover>
|
||||
</label>
|
||||
|
||||
<div ng-repeat="fil in ctrl.target.filters track by $index" class="gf-form-label">
|
||||
{{fil.tagk}} = {{fil.type}}({{fil.filter}}) , groupBy = {{fil.groupBy}}
|
||||
<a ng-click="ctrl.editFilter(fil, $index)">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a ng-click="ctrl.removeFilter($index)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</div>
|
||||
<label class="gf-form-label query-keyword" ng-hide="ctrl.addFilterMode">
|
||||
<a ng-click="ctrl.addFilter()">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline" ng-show="ctrl.addFilterMode">
|
||||
<div class="gf-form">
|
||||
<input type="text" class="gf-form-input" spellcheck='false'
|
||||
bs-typeahead="ctrl.suggestTagKeys" data-min-length=0 data-items=100
|
||||
ng-model="ctrl.target.currentFilterKey" placeholder="key">
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword">Type</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select ng-model="ctrl.target.currentFilterType" class="gf-form-input" ng-options="filType for filType in ctrl.filterTypes">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<input type="text" class="gf-form-input" spellcheck='false' bs-typeahead="ctrl.suggestTagValues" data-min-length=0 data-items=100 ng-model="ctrl.target.currentFilterValue" placeholder="filter">
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<gf-form-switch class="gf-form"
|
||||
label="Group by"
|
||||
label-class="query-keyword"
|
||||
checked="ctrl.target.currentFilterGroupBy"
|
||||
on-change="ctrl.targetBlur()">
|
||||
</gf-form-switch>
|
||||
|
||||
<div class="gf-form" ng-show="ctrl.addFilterMode">
|
||||
<label class="gf-form-label" ng-show="ctrl.errors.filters">
|
||||
<a bs-tooltip="ctrl.errors.filters" style="color: rgb(229, 189, 28)" >
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</label>
|
||||
<label class="gf-form-label">
|
||||
<a ng-click="ctrl.addFilter()" ng-hide="ctrl.errors.filters">add filter</a>
|
||||
<a ng-click="ctrl.closeAddFilterMode()">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-8">
|
||||
Tags
|
||||
<info-popover mode="right-normal" ng-if="ctrl.tsdbVersion >= 2">
|
||||
Please use filters, tags are deprecated in opentsdb 2.2
|
||||
</info-popover>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-repeat="(key, value) in ctrl.target.tags track by $index" class="gf-form">
|
||||
<label class="gf-form-label">
|
||||
{{key}} = {{value}}
|
||||
<a ng-click="ctrl.editTag(key, value)">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a ng-click="ctrl.removeTag(key)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-hide="ctrl.addTagMode">
|
||||
<label class="gf-form-label query-keyword">
|
||||
<a ng-click="ctrl.addTag()"><i class="fa fa-plus"></i></a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-show="ctrl.addTagMode">
|
||||
<input type="text"
|
||||
class="gf-form-input" spellcheck='false'
|
||||
bs-typeahead="ctrl.suggestTagKeys" data-min-length=0 data-items=100
|
||||
ng-model="ctrl.target.currentTagKey" placeholder="key">
|
||||
</input>
|
||||
|
||||
<input type="text" class="gf-form-input"
|
||||
spellcheck='false' bs-typeahead="ctrl.suggestTagValues"
|
||||
data-min-length=0 data-items=100 ng-model="ctrl.target.currentTagValue" placeholder="value">
|
||||
</input>
|
||||
|
||||
<label class="gf-form-label" ng-show="ctrl.errors.tags">
|
||||
<a bs-tooltip="ctrl.errors.tags" style="color: rgb(229, 189, 28)" >
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</label>
|
||||
<label class="gf-form-label" >
|
||||
<a ng-click="ctrl.addTag()" ng-hide="ctrl.errors.tags">add tag</a>
|
||||
<a ng-click="ctrl.closeAddTagMode()"><i class="fa fa-remove"></i></a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<gf-form-switch class="gf-form" label="Rate" label-class="width-8 query-keyword" checked="ctrl.target.shouldComputeRate" on-change="ctrl.targetBlur()">
|
||||
</gf-form-switch>
|
||||
|
||||
<gf-form-switch ng-hide="!ctrl.target.shouldComputeRate"
|
||||
class="gf-form" label="Counter" label-class="query-keyword" checked="ctrl.target.isCounter" on-change="ctrl.targetBlur()">
|
||||
</gf-form-switch>
|
||||
|
||||
|
||||
<div class="gf-form" ng-hide="!ctrl.target.isCounter || !ctrl.target.shouldComputeRate">
|
||||
<label class="gf-form-label query-keyword">Counter Max</label>
|
||||
<input type="text" class="gf-form-input"
|
||||
ng-disabled="!ctrl.target.shouldComputeRate"
|
||||
ng-model="ctrl.target.counterMax" spellcheck='false'
|
||||
placeholder="max value" ng-model-onblur
|
||||
ng-blur="ctrl.targetBlur()">
|
||||
</input>
|
||||
|
||||
<label class="gf-form-label query-keyword">Reset Value</label>
|
||||
<input type="text" class="tight-form-input input-small"
|
||||
ng-disabled="!ctrl.target.shouldComputeRate"
|
||||
ng-model="ctrl.target.counterResetValue" spellcheck='false'
|
||||
placeholder="reset value" ng-model-onblur
|
||||
ng-blur="ctrl.targetBlur()">
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
</query-editor-row>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.timely-app-heading {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
System.register(['lodash'], function (_export, _context) {
|
||||
"use strict";
|
||||
|
||||
var _, DatasourceConfigCtrl;
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [function (_lodash) {
|
||||
_ = _lodash.default;
|
||||
}],
|
||||
execute: function () {
|
||||
_export('DatasourceConfigCtrl', DatasourceConfigCtrl = function DatasourceConfigCtrl() {
|
||||
_classCallCheck(this, DatasourceConfigCtrl);
|
||||
|
||||
this.current.jsonData = this.current.jsonData || {};
|
||||
this.current.access = 'direct';
|
||||
this.current.basicAuth = false;
|
||||
this.current.withCredentials = true;
|
||||
this.current.suggestHost = 'localhost';
|
||||
});
|
||||
|
||||
_export('DatasourceConfigCtrl', DatasourceConfigCtrl);
|
||||
|
||||
DatasourceConfigCtrl.templateUrl = 'datasource/partials/config.html';
|
||||
}
|
||||
};
|
||||
});
|
||||
//# sourceMappingURL=config.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/datasource/config.js"],"names":["_","DatasourceConfigCtrl","current","jsonData","access","basicAuth","withCredentials","suggestHost","templateUrl"],"mappings":";;;;;;;;;;;;;;;AAAOA,O;;;sCAGMC,oB,GAEX,gCAAc;AAAA;;AACZ,aAAKC,OAAL,CAAaC,QAAb,GAAwB,KAAKD,OAAL,CAAaC,QAAb,IAAyB,EAAjD;AACA,aAAKD,OAAL,CAAaE,MAAb,GAAsB,QAAtB;AACA,aAAKF,OAAL,CAAaG,SAAb,GAAyB,KAAzB;AACA,aAAKH,OAAL,CAAaI,eAAb,GAA+B,IAA/B;AACA,aAAKJ,OAAL,CAAaK,WAAb,GAA2B,WAA3B;AACD,O;;;;AAEHN,2BAAqBO,WAArB,GAAmC,iCAAnC","file":"config.js","sourcesContent":["import _ from 'lodash';\n\n\nexport class DatasourceConfigCtrl {\n\n constructor() {\n this.current.jsonData = this.current.jsonData || {};\n this.current.access = 'direct';\n this.current.basicAuth = false;\n this.current.withCredentials = true;\n this.current.suggestHost = 'localhost';\n }\n}\nDatasourceConfigCtrl.templateUrl = 'datasource/partials/config.html';\n"]}
|
||||
@@ -0,0 +1,786 @@
|
||||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_TIMELY",
|
||||
"label": "Timely",
|
||||
"description": "",
|
||||
"type": "datasource",
|
||||
"pluginId": "timely-datasource",
|
||||
"pluginName": "Timely"
|
||||
}
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "singlestat",
|
||||
"name": "Singlestat",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "graph",
|
||||
"name": "Graph",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "3.1.0"
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
"id": "timely-datasource",
|
||||
"name": "Timely",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"id": null,
|
||||
"title": "Timely Status",
|
||||
"tags": [],
|
||||
"style": "dark",
|
||||
"timezone": "browser",
|
||||
"editable": true,
|
||||
"hideControls": false,
|
||||
"sharedCrosshair": false,
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "150",
|
||||
"panels": [
|
||||
{
|
||||
"cacheTimeout": null,
|
||||
"colorBackground": false,
|
||||
"colorValue": false,
|
||||
"colors": [
|
||||
"rgba(245, 54, 54, 0.9)",
|
||||
"rgba(237, 129, 40, 0.89)",
|
||||
"rgba(50, 172, 45, 0.97)"
|
||||
],
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"format": "none",
|
||||
"gauge": {
|
||||
"maxValue": 100000,
|
||||
"minValue": 0,
|
||||
"show": true,
|
||||
"thresholdLabels": false,
|
||||
"thresholdMarkers": false
|
||||
},
|
||||
"height": "150",
|
||||
"hideTimeOverride": true,
|
||||
"id": 8,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
{
|
||||
"name": "value to text",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"name": "range to text",
|
||||
"value": 2
|
||||
}
|
||||
],
|
||||
"maxDataPoints": 100,
|
||||
"nullPointMode": "connected",
|
||||
"nullText": null,
|
||||
"postfix": "",
|
||||
"postfixFontSize": "50%",
|
||||
"prefix": "",
|
||||
"prefixFontSize": "50%",
|
||||
"rangeMaps": [
|
||||
{
|
||||
"from": "null",
|
||||
"text": "N/A",
|
||||
"to": "null"
|
||||
}
|
||||
],
|
||||
"span": 4,
|
||||
"sparkline": {
|
||||
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||
"full": false,
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "count",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "timely.metrics.received",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": "",
|
||||
"timeFrom": null,
|
||||
"timeShift": "1m",
|
||||
"title": "Metrics Received Last Minute",
|
||||
"transparent": true,
|
||||
"type": "singlestat",
|
||||
"valueFontSize": "80%",
|
||||
"valueMaps": [
|
||||
{
|
||||
"op": "=",
|
||||
"text": "N/A",
|
||||
"value": "null"
|
||||
}
|
||||
],
|
||||
"valueName": "avg"
|
||||
},
|
||||
{
|
||||
"cacheTimeout": null,
|
||||
"colorBackground": false,
|
||||
"colorValue": false,
|
||||
"colors": [
|
||||
"rgba(245, 54, 54, 0.9)",
|
||||
"rgba(237, 129, 40, 0.89)",
|
||||
"rgba(50, 172, 45, 0.97)"
|
||||
],
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"format": "none",
|
||||
"gauge": {
|
||||
"maxValue": 100,
|
||||
"minValue": 0,
|
||||
"show": false,
|
||||
"thresholdLabels": false,
|
||||
"thresholdMarkers": true
|
||||
},
|
||||
"height": "150",
|
||||
"hideTimeOverride": true,
|
||||
"id": 9,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
{
|
||||
"name": "value to text",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"name": "range to text",
|
||||
"value": 2
|
||||
}
|
||||
],
|
||||
"maxDataPoints": 100,
|
||||
"nullPointMode": "connected",
|
||||
"nullText": null,
|
||||
"postfix": "",
|
||||
"postfixFontSize": "50%",
|
||||
"prefix": "",
|
||||
"prefixFontSize": "50%",
|
||||
"rangeMaps": [
|
||||
{
|
||||
"from": "null",
|
||||
"text": "N/A",
|
||||
"to": "null"
|
||||
}
|
||||
],
|
||||
"span": 4,
|
||||
"sparkline": {
|
||||
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||
"full": false,
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "count",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "count",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "sys.collectd.queue_length",
|
||||
"refId": "A",
|
||||
"tags": {}
|
||||
}
|
||||
],
|
||||
"thresholds": "",
|
||||
"timeShift": "1m",
|
||||
"title": "CollectD Instances Reporting",
|
||||
"transparent": true,
|
||||
"type": "singlestat",
|
||||
"valueFontSize": "80%",
|
||||
"valueMaps": [
|
||||
{
|
||||
"op": "=",
|
||||
"text": "N/A",
|
||||
"value": "null"
|
||||
}
|
||||
],
|
||||
"valueName": "avg"
|
||||
},
|
||||
{
|
||||
"cacheTimeout": null,
|
||||
"colorBackground": false,
|
||||
"colorValue": false,
|
||||
"colors": [
|
||||
"rgba(245, 54, 54, 0.9)",
|
||||
"rgba(237, 129, 40, 0.89)",
|
||||
"rgba(50, 172, 45, 0.97)"
|
||||
],
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"format": "none",
|
||||
"gauge": {
|
||||
"maxValue": 100,
|
||||
"minValue": 0,
|
||||
"show": false,
|
||||
"thresholdLabels": false,
|
||||
"thresholdMarkers": true
|
||||
},
|
||||
"height": "150",
|
||||
"hideTimeOverride": true,
|
||||
"id": 10,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
{
|
||||
"name": "value to text",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"name": "range to text",
|
||||
"value": 2
|
||||
}
|
||||
],
|
||||
"maxDataPoints": 100,
|
||||
"nullPointMode": "connected",
|
||||
"nullText": null,
|
||||
"postfix": "",
|
||||
"postfixFontSize": "50%",
|
||||
"prefix": "",
|
||||
"prefixFontSize": "50%",
|
||||
"rangeMaps": [
|
||||
{
|
||||
"from": "null",
|
||||
"text": "N/A",
|
||||
"to": "null"
|
||||
}
|
||||
],
|
||||
"span": 4,
|
||||
"sparkline": {
|
||||
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||
"full": false,
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "sys.collectd.derive.dropped",
|
||||
"refId": "A",
|
||||
"tags": {}
|
||||
}
|
||||
],
|
||||
"thresholds": "",
|
||||
"timeShift": "1m",
|
||||
"title": "Metrics Dropped By CollectD",
|
||||
"transparent": true,
|
||||
"type": "singlestat",
|
||||
"valueFontSize": "80%",
|
||||
"valueMaps": [
|
||||
{
|
||||
"op": "=",
|
||||
"text": "N/A",
|
||||
"value": "null"
|
||||
}
|
||||
],
|
||||
"valueName": "avg"
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 3,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "timely.keys.metric.inserted",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Metrics Inserted",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "none",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 4,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "timely.keys.meta.inserted",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Meta Inserted",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "none",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 5,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "timely.metrics.received",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Metrics Received",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "none",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 7,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 6,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "sys.collectd.cache_size",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CollectD Cache Sizes",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 11,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 6,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "sys.collectd.queue_length",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CollectD Queue Length",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
}
|
||||
],
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"now": true,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"refresh": "1m",
|
||||
"schemaVersion": 12,
|
||||
"version": 8,
|
||||
"links": [],
|
||||
"gnetId": null
|
||||
}
|
||||
@@ -0,0 +1,434 @@
|
||||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_NAME",
|
||||
"type": "datasource",
|
||||
"pluginId": "timely-datasource"
|
||||
}
|
||||
],
|
||||
"id": null,
|
||||
"title": "Timely Standalone Demo",
|
||||
"originalTitle": "Timely Standalone Demo",
|
||||
"tags": ["imported"],
|
||||
"style": "dark",
|
||||
"timezone": "browser",
|
||||
"editable": true,
|
||||
"hideControls": false,
|
||||
"sharedCrosshair": false,
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)",
|
||||
"thresholdLine": false
|
||||
},
|
||||
"id": 1,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
"current": true,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": true,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "",
|
||||
"errors": {},
|
||||
"isCounter": false,
|
||||
"metric": "timely.metrics.received",
|
||||
"refId": "A",
|
||||
"shouldComputeRate": false
|
||||
},
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "",
|
||||
"errors": {},
|
||||
"metric": "timely.keys.meta.inserted",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "",
|
||||
"errors": {},
|
||||
"metric": "timely.keys.metric.inserted",
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Status",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": false,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": "Keys",
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "Row"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 2,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "max",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "max",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1s",
|
||||
"metric": "sys.cpu.user",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CPU User by Host",
|
||||
"tooltip": {
|
||||
"msResolution": false,
|
||||
"shared": true,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 3,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "max",
|
||||
"alias": "$tag_rack",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "max",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1s",
|
||||
"metric": "sys.cpu.user",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"rack": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CPU User by Rack",
|
||||
"tooltip": {
|
||||
"msResolution": false,
|
||||
"shared": true,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 4,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "max",
|
||||
"alias": "",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "max",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1s",
|
||||
"metric": "sys.cpu.user",
|
||||
"refId": "A",
|
||||
"tags": {}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Cumulative CPU",
|
||||
"tooltip": {
|
||||
"msResolution": false,
|
||||
"shared": true,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
}
|
||||
],
|
||||
"time": {
|
||||
"from": "now-15m",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"now": true,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"refresh": "5s",
|
||||
"schemaVersion": 12,
|
||||
"version": 1,
|
||||
"links": []
|
||||
}
|
||||
@@ -0,0 +1,533 @@
|
||||
'use strict';
|
||||
|
||||
System.register(['lodash', 'angular', '../../../app/core/utils/datemath'], function (_export, _context) {
|
||||
"use strict";
|
||||
|
||||
var _, angular, dateMath, _createClass, TimelyDatasource;
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [function (_lodash) {
|
||||
_ = _lodash.default;
|
||||
}, function (_angular) {
|
||||
angular = _angular.default;
|
||||
}, function (_appCoreUtilsDatemath) {
|
||||
dateMath = _appCoreUtilsDatemath;
|
||||
}],
|
||||
execute: function () {
|
||||
_createClass = function () {
|
||||
function defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return function (Constructor, protoProps, staticProps) {
|
||||
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
};
|
||||
}();
|
||||
|
||||
_export('TimelyDatasource', TimelyDatasource = function () {
|
||||
|
||||
// arguments injected by angular
|
||||
function TimelyDatasource(instanceSettings, $q, backendSrv, templateSrv, contextSrv) {
|
||||
_classCallCheck(this, TimelyDatasource);
|
||||
|
||||
this.type = instanceSettings.type;
|
||||
this.name = instanceSettings.name;
|
||||
|
||||
this.jsonData = instanceSettings.jsonData || {};
|
||||
this.httpsPort = instanceSettings.jsonData.httpsPort;
|
||||
this.timelyHost = instanceSettings.jsonData.timelyHost;
|
||||
this.wsPort = instanceSettings.jsonData.wsPort;
|
||||
if (!instanceSettings.jsonData.basicAuths) {
|
||||
instanceSettings.jsonData.basicAuths = false;
|
||||
}
|
||||
|
||||
this.url = this._makeHttpsUrl();
|
||||
this.withCredentials = true;
|
||||
this.basicAuth = false;
|
||||
|
||||
this.supportMetrics = true;
|
||||
this.tagKeys = {};
|
||||
|
||||
this.$q = $q;
|
||||
this.backendSrv = backendSrv;
|
||||
this.templateSrv = templateSrv;
|
||||
this.contextSrv = contextSrv;
|
||||
|
||||
this.aggregatorsPromise = null;
|
||||
this.filterTypesPromise = null;
|
||||
}
|
||||
|
||||
// REQUIRED INTERFACE
|
||||
|
||||
|
||||
_createClass(TimelyDatasource, [{
|
||||
key: 'query',
|
||||
value: function query(options) {
|
||||
var start = this.convertToTSDBTime(options.rangeRaw.from, false);
|
||||
var end = this.convertToTSDBTime(options.rangeRaw.to, true);
|
||||
var qs = [];
|
||||
|
||||
_.each(options.targets, function (target) {
|
||||
if (!target.metric) {
|
||||
return;
|
||||
}
|
||||
qs.push(this.convertTargetToQuery(target, options));
|
||||
}.bind(this));
|
||||
|
||||
var queries = _.compact(qs);
|
||||
|
||||
// No valid targets, return the empty result to save a round trip.
|
||||
if (_.isEmpty(queries)) {
|
||||
var d = this.$q.defer();
|
||||
d.resolve({ data: [] });
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
var groupByTags = {};
|
||||
_.each(queries, function (query) {
|
||||
if (query.filters && query.filters.length > 0) {
|
||||
_.each(query.filters, function (val) {
|
||||
groupByTags[val.tagk] = true;
|
||||
});
|
||||
} else {
|
||||
_.each(query.tags, function (val, key) {
|
||||
groupByTags[key] = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return this.performTimeSeriesQuery(queries, start, end).then(function (response) {
|
||||
var metricToTargetMapping = this.mapMetricsToTargets(response.data, options);
|
||||
var result = _.map(response.data, function (metricData, index) {
|
||||
index = metricToTargetMapping[index];
|
||||
if (index === -1) {
|
||||
index = 0;
|
||||
}
|
||||
this._saveTagKeys(metricData);
|
||||
|
||||
return this.transformMetricData(metricData, groupByTags, options.targets[index], options);
|
||||
}.bind(this));
|
||||
return { data: result };
|
||||
}.bind(this));
|
||||
}
|
||||
}, {
|
||||
key: 'testDatasource',
|
||||
value: function testDatasource() {
|
||||
return this.backendSrv.datasourceRequest({
|
||||
url: this.url + '/version',
|
||||
method: 'GET'
|
||||
}).then(function (response) {
|
||||
if (response.status === 200) {
|
||||
return { status: "success", message: "Data source is working", title: "Success" };
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: 'annotationQuery',
|
||||
value: function annotationQuery(options) {
|
||||
var start = convertToTSDBTime(options.rangeRaw.from, false);
|
||||
var end = convertToTSDBTime(options.rangeRaw.to, true);
|
||||
var qs = [];
|
||||
var eventList = [];
|
||||
|
||||
qs.push({ aggregator: "sum", metric: options.annotation.target });
|
||||
|
||||
var queries = _.compact(qs);
|
||||
|
||||
return this.performTimeSeriesQuery(queries, start, end).then(function (results) {
|
||||
if (results.data[0]) {
|
||||
var annotationObject = results.data[0].annotations;
|
||||
if (options.annotation.isGlobal) {
|
||||
annotationObject = results.data[0].globalAnnotations;
|
||||
}
|
||||
if (annotationObject) {
|
||||
_.each(annotationObject, function (annotation) {
|
||||
var event = {
|
||||
title: annotation.description,
|
||||
time: Math.floor(annotation.startTime) * 1000,
|
||||
text: annotation.notes,
|
||||
annotation: options.annotation
|
||||
};
|
||||
|
||||
eventList.push(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
return eventList;
|
||||
}.bind(this));
|
||||
}
|
||||
}, {
|
||||
key: 'metricFindQuery',
|
||||
value: function metricFindQuery(query) {
|
||||
if (!query) {
|
||||
return this.$q.when([]);
|
||||
}
|
||||
|
||||
var interpolated;
|
||||
try {
|
||||
interpolated = this.templateSrv.replace(query);
|
||||
} catch (err) {
|
||||
return this.$q.reject(err);
|
||||
}
|
||||
|
||||
var responseTransform = function responseTransform(result) {
|
||||
return _.map(result, function (value) {
|
||||
return { text: value };
|
||||
});
|
||||
};
|
||||
|
||||
var metrics_regex = /metrics\((.*)\)/;
|
||||
var tag_names_regex = /tag_names\((.*)\)/;
|
||||
var tag_values_regex = /tag_values\((.*?),\s?(.*)\)/;
|
||||
var tag_names_suggest_regex = /suggest_tagk\((.*)\)/;
|
||||
var tag_values_suggest_regex = /suggest_tagv\((.*)\)/;
|
||||
|
||||
var metrics_query = interpolated.match(metrics_regex);
|
||||
if (metrics_query) {
|
||||
return this._performSuggestQuery(metrics_query[1], 'metrics').then(responseTransform);
|
||||
}
|
||||
|
||||
var tag_names_query = interpolated.match(tag_names_regex);
|
||||
if (tag_names_query) {
|
||||
return this._performMetricKeyLookup(tag_names_query[1]).then(responseTransform);
|
||||
}
|
||||
|
||||
var tag_values_query = interpolated.match(tag_values_regex);
|
||||
if (tag_values_query) {
|
||||
return this._performMetricKeyValueLookup(tag_values_query[1], tag_values_query[2]).then(responseTransform);
|
||||
}
|
||||
|
||||
var tag_names_suggest_query = interpolated.match(tag_names_suggest_regex);
|
||||
if (tag_names_suggest_query) {
|
||||
return this._performSuggestQuery(tag_names_suggest_query[1], 'tagk').then(responseTransform);
|
||||
}
|
||||
|
||||
var tag_values_suggest_query = interpolated.match(tag_values_suggest_regex);
|
||||
if (tag_values_suggest_query) {
|
||||
return this._performSuggestQuery(tag_values_suggest_query[1], 'tagv').then(responseTransform);
|
||||
}
|
||||
|
||||
return this.$q.when([]);
|
||||
}
|
||||
}, {
|
||||
key: 'performTimeSeriesQuery',
|
||||
value: function performTimeSeriesQuery(queries, start, end) {
|
||||
var msResolution = true;
|
||||
|
||||
var reqBody = {
|
||||
start: start,
|
||||
queries: queries,
|
||||
msResolution: msResolution,
|
||||
globalAnnotations: true,
|
||||
showQuery: true
|
||||
};
|
||||
|
||||
// Relative queries (e.g. last hour) don't include an end time
|
||||
if (end) {
|
||||
reqBody.end = end;
|
||||
}
|
||||
|
||||
var options = {
|
||||
method: 'POST',
|
||||
url: this.url + '/api/query',
|
||||
data: reqBody
|
||||
};
|
||||
|
||||
this._addCredentialOptions(options);
|
||||
|
||||
// In case the backend is 3rd-party hosted and does not suport OPTIONS, urlencoded requests
|
||||
// go as POST rather than OPTIONS+POST
|
||||
options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
||||
|
||||
return this.backendSrv.datasourceRequest(options);
|
||||
}
|
||||
}, {
|
||||
key: '_saveTagKeys',
|
||||
value: function _saveTagKeys(metricData) {
|
||||
var tagKeys = Object.keys(metricData.tags);
|
||||
_.each(metricData.aggregateTags, function (tag) {
|
||||
tagKeys.push(tag);
|
||||
});
|
||||
|
||||
this.tagKeys[metricData.metric] = tagKeys;
|
||||
}
|
||||
}, {
|
||||
key: 'suggestTagKeys',
|
||||
value: function suggestTagKeys(metric) {
|
||||
return this.$q.when(this.tagKeys[metric] || []);
|
||||
}
|
||||
}, {
|
||||
key: '_performSuggestQuery',
|
||||
value: function _performSuggestQuery(query, type) {
|
||||
return this._get('/api/suggest', { type: type, q: query, max: 1000 }).then(function (result) {
|
||||
return result.data;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: '_performMetricKeyValueLookup',
|
||||
value: function _performMetricKeyValueLookup(metric, keys) {
|
||||
|
||||
if (!metric || !keys) {
|
||||
return $q.when([]);
|
||||
}
|
||||
|
||||
var keysArray = keys.split(",").map(function (key) {
|
||||
return key.trim();
|
||||
});
|
||||
var key = keysArray[0];
|
||||
var keysQuery = key + "=.*";
|
||||
|
||||
if (keysArray.length > 1) {
|
||||
keysQuery += "," + keysArray.splice(1).join(",");
|
||||
}
|
||||
|
||||
var m = metric + "{" + keysQuery + "}";
|
||||
|
||||
return this._get('/api/search/lookup', { m: m, limit: 3000 }).then(function (result) {
|
||||
result = result.data.results;
|
||||
var tagvs = [];
|
||||
_.each(result, function (r) {
|
||||
if (tagvs.indexOf(r.tags[key]) === -1) {
|
||||
tagvs.push(r.tags[key]);
|
||||
}
|
||||
});
|
||||
return tagvs;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: '_performMetricKeyLookup',
|
||||
value: function _performMetricKeyLookup(metric) {
|
||||
if (!metric) {
|
||||
return this.$q.when([]);
|
||||
}
|
||||
|
||||
return this._get('/api/search/lookup', { m: metric, limit: 1000 }).then(function (result) {
|
||||
result = result.data.results;
|
||||
var tagks = [];
|
||||
_.each(result, function (r) {
|
||||
_.each(r.tags, function (tagv, tagk) {
|
||||
if (tagks.indexOf(tagk) === -1) {
|
||||
tagks.push(tagk);
|
||||
}
|
||||
});
|
||||
});
|
||||
return tagks;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: '_get',
|
||||
value: function _get(relativeUrl, params) {
|
||||
var options = {
|
||||
method: 'GET',
|
||||
url: this.url + relativeUrl,
|
||||
params: params
|
||||
};
|
||||
|
||||
this._addCredentialOptions(options);
|
||||
|
||||
return this.backendSrv.datasourceRequest(options);
|
||||
}
|
||||
}, {
|
||||
key: '_addCredentialOptions',
|
||||
value: function _addCredentialOptions(options) {
|
||||
if (this.basicAuth || this.withCredentials) {
|
||||
options.withCredentials = true;
|
||||
}
|
||||
if (this.basicAuth) {
|
||||
options.headers = { "Authorization": this.basicAuth };
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'getAggregators',
|
||||
value: function getAggregators() {
|
||||
if (this.aggregatorsPromise) {
|
||||
return this.aggregatorsPromise;
|
||||
}
|
||||
|
||||
this.aggregatorsPromise = this._get('/api/aggregators').then(function (result) {
|
||||
if (result.data && _.isArray(result.data)) {
|
||||
return result.data.sort();
|
||||
}
|
||||
return [];
|
||||
});
|
||||
return this.aggregatorsPromise;
|
||||
}
|
||||
}, {
|
||||
key: 'getFilterTypes',
|
||||
value: function getFilterTypes() {
|
||||
if (this.filterTypesPromise) {
|
||||
return this.filterTypesPromise;
|
||||
}
|
||||
|
||||
// filters are for a newer opentsdb api version than Timely originally copied.
|
||||
this.filterTypesPromise = this._get('/api/aggregators').then(function (result) {
|
||||
return [];
|
||||
});
|
||||
return this.filterTypesPromise;
|
||||
}
|
||||
}, {
|
||||
key: 'transformMetricData',
|
||||
value: function transformMetricData(md, groupByTags, target, options) {
|
||||
var metricLabel = this.createMetricLabel(md, target, groupByTags, options);
|
||||
var dps = [];
|
||||
|
||||
// TSDB returns datapoints has a hash of ts => value.
|
||||
// Can't use _.pairs(invert()) because it stringifies keys/values
|
||||
_.each(md.dps, function (v, k) {
|
||||
dps.push([v, k * 1]);
|
||||
});
|
||||
|
||||
return { target: metricLabel, datapoints: dps };
|
||||
}
|
||||
}, {
|
||||
key: 'createMetricLabel',
|
||||
value: function createMetricLabel(md, target, groupByTags, options) {
|
||||
if (target.alias) {
|
||||
var scopedVars = _.clone(options.scopedVars || {});
|
||||
_.each(md.tags, function (value, key) {
|
||||
scopedVars['tag_' + key] = { value: value };
|
||||
});
|
||||
return this.templateSrv.replace(target.alias, scopedVars);
|
||||
}
|
||||
|
||||
var label = md.metric;
|
||||
var tagData = [];
|
||||
|
||||
if (!_.isEmpty(md.tags)) {
|
||||
_.each(_.pairs(md.tags), function (tag) {
|
||||
if (_.has(groupByTags, tag[0])) {
|
||||
tagData.push(tag[0] + "=" + tag[1]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!_.isEmpty(tagData)) {
|
||||
label += "{" + tagData.join(", ") + "}";
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
}, {
|
||||
key: 'convertTargetToQuery',
|
||||
value: function convertTargetToQuery(target, options) {
|
||||
if (!target.metric || target.hide) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var query = {
|
||||
metric: this.templateSrv.replace(target.metric, options.scopedVars),
|
||||
aggregator: "avg"
|
||||
};
|
||||
|
||||
if (target.aggregator) {
|
||||
query.aggregator = this.templateSrv.replace(target.aggregator);
|
||||
}
|
||||
|
||||
if (target.shouldComputeRate) {
|
||||
query.rate = true;
|
||||
query.rateOptions = {
|
||||
counter: !!target.isCounter
|
||||
};
|
||||
|
||||
if (target.counterMax && target.counterMax.length) {
|
||||
query.rateOptions.counterMax = parseInt(target.counterMax);
|
||||
}
|
||||
|
||||
if (target.counterResetValue && target.counterResetValue.length) {
|
||||
query.rateOptions.resetValue = parseInt(target.counterResetValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (!target.disableDownsampling) {
|
||||
var interval = this.templateSrv.replace(target.downsampleInterval || options.interval);
|
||||
|
||||
if (interval.match(/\.[0-9]+s/)) {
|
||||
interval = parseFloat(interval) * 1000 + "ms";
|
||||
}
|
||||
|
||||
query.downsample = interval + "-" + target.downsampleAggregator;
|
||||
|
||||
if (target.downsampleFillPolicy && target.downsampleFillPolicy !== "none") {
|
||||
query.downsample += "-" + target.downsampleFillPolicy;
|
||||
}
|
||||
}
|
||||
|
||||
if (target.filters && target.filters.length > 0) {
|
||||
query.filters = angular.copy(target.filters);
|
||||
if (query.filters) {
|
||||
for (var filter_key in query.filters) {
|
||||
query.filters[filter_key].filter = this.templateSrv.replace(query.filters[filter_key].filter, options.scopedVars, 'pipe');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
query.tags = angular.copy(target.tags);
|
||||
if (query.tags) {
|
||||
for (var tag_key in query.tags) {
|
||||
query.tags[tag_key] = this.templateSrv.replace(query.tags[tag_key], options.scopedVars, 'pipe');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
}, {
|
||||
key: 'mapMetricsToTargets',
|
||||
value: function mapMetricsToTargets(metrics, options) {
|
||||
var interpolatedTagValue;
|
||||
return _.map(metrics, function (metricData) {
|
||||
return _.findIndex(options.targets, function (target) {
|
||||
if (target.filters && target.filters.length > 0) {
|
||||
return target.metric === metricData.metric;
|
||||
} else {
|
||||
return target.metric === metricData.metric && _.all(target.tags, function (tagV, tagK) {
|
||||
interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe');
|
||||
return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === "*";
|
||||
}.bind(this));
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
}, {
|
||||
key: 'convertToTSDBTime',
|
||||
value: function convertToTSDBTime(date, roundUp) {
|
||||
if (date === 'now') {
|
||||
return null;
|
||||
}
|
||||
|
||||
date = dateMath.parse(date, roundUp);
|
||||
return date.valueOf();
|
||||
}
|
||||
}, {
|
||||
key: '_makeHttpsUrl',
|
||||
value: function _makeHttpsUrl() {
|
||||
return 'https://' + this.timelyHost + ':' + this.httpsPort;
|
||||
}
|
||||
}, {
|
||||
key: '_makeWsUrl',
|
||||
value: function _makeWsUrl() {
|
||||
return 'ws://' + this.timelyHost + ':' + this.wsPort;
|
||||
}
|
||||
}]);
|
||||
|
||||
return TimelyDatasource;
|
||||
}());
|
||||
|
||||
_export('TimelyDatasource', TimelyDatasource);
|
||||
}
|
||||
};
|
||||
});
|
||||
//# sourceMappingURL=datasource.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/datasource/datasource.js"],"names":["_","angular","dateMath","TimelyDatasource","instanceSettings","$q","backendSrv","templateSrv","contextSrv","type","name","jsonData","httpsPort","timelyHost","wsPort","basicAuths","url","_makeHttpsUrl","withCredentials","basicAuth","supportMetrics","tagKeys","aggregatorsPromise","filterTypesPromise","options","start","convertToTSDBTime","rangeRaw","from","end","to","qs","each","targets","target","metric","push","convertTargetToQuery","bind","queries","compact","isEmpty","d","defer","resolve","data","promise","groupByTags","query","filters","length","val","tagk","tags","key","performTimeSeriesQuery","then","response","metricToTargetMapping","mapMetricsToTargets","result","map","metricData","index","_saveTagKeys","transformMetricData","datasourceRequest","method","status","message","title","eventList","aggregator","annotation","results","annotationObject","annotations","isGlobal","globalAnnotations","event","description","time","Math","floor","startTime","text","notes","when","interpolated","replace","err","reject","responseTransform","value","metrics_regex","tag_names_regex","tag_values_regex","tag_names_suggest_regex","tag_values_suggest_regex","metrics_query","match","_performSuggestQuery","tag_names_query","_performMetricKeyLookup","tag_values_query","_performMetricKeyValueLookup","tag_names_suggest_query","tag_values_suggest_query","msResolution","reqBody","showQuery","_addCredentialOptions","headers","Object","keys","aggregateTags","tag","_get","q","max","keysArray","split","trim","keysQuery","splice","join","m","limit","tagvs","r","indexOf","tagks","tagv","relativeUrl","params","isArray","sort","md","metricLabel","createMetricLabel","dps","v","k","datapoints","alias","scopedVars","clone","label","tagData","pairs","has","hide","shouldComputeRate","rate","rateOptions","counter","isCounter","counterMax","parseInt","counterResetValue","resetValue","disableDownsampling","interval","downsampleInterval","parseFloat","downsample","downsampleAggregator","downsampleFillPolicy","copy","filter_key","filter","tag_key","metrics","interpolatedTagValue","findIndex","all","tagV","tagK","date","roundUp","parse","valueOf"],"mappings":"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 truncated
|
||||
@@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
System.register(['./datasource', './query_ctrl', './config'], function (_export, _context) {
|
||||
"use strict";
|
||||
|
||||
var TimelyDatasource, TimelyQueryCtrl, DatasourceConfigCtrl, TimelyAnnotationsQueryCtrl;
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [function (_datasource) {
|
||||
TimelyDatasource = _datasource.TimelyDatasource;
|
||||
}, function (_query_ctrl) {
|
||||
TimelyQueryCtrl = _query_ctrl.TimelyQueryCtrl;
|
||||
}, function (_config) {
|
||||
DatasourceConfigCtrl = _config.DatasourceConfigCtrl;
|
||||
}],
|
||||
execute: function () {
|
||||
_export('AnnotationsQueryCtrl', TimelyAnnotationsQueryCtrl = function TimelyAnnotationsQueryCtrl() {
|
||||
_classCallCheck(this, TimelyAnnotationsQueryCtrl);
|
||||
});
|
||||
|
||||
TimelyAnnotationsQueryCtrl.templateUrl = 'components/annotations.editor.html';
|
||||
|
||||
_export('Datasource', TimelyDatasource);
|
||||
|
||||
_export('QueryCtrl', TimelyQueryCtrl);
|
||||
|
||||
_export('ConfigCtrl', DatasourceConfigCtrl);
|
||||
|
||||
_export('AnnotationsQueryCtrl', TimelyAnnotationsQueryCtrl);
|
||||
}
|
||||
};
|
||||
});
|
||||
//# sourceMappingURL=module.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/datasource/module.js"],"names":["TimelyDatasource","TimelyQueryCtrl","DatasourceConfigCtrl","TimelyAnnotationsQueryCtrl","templateUrl"],"mappings":";;;;;;;;;;;;;;;AAAQA,sB,eAAAA,gB;;AACAC,qB,eAAAA,e;;AACAC,0B,WAAAA,oB;;;sCAEFC,0B;;;;AACNA,iCAA2BC,WAA3B,GAAyC,oCAAzC;;4BAGEJ,gB;;2BACAC,e;;4BACAC,oB;;sCACAC,0B","file":"module.js","sourcesContent":["import {TimelyDatasource} from './datasource';\nimport {TimelyQueryCtrl} from './query_ctrl';\nimport {DatasourceConfigCtrl} from './config';\n\nclass TimelyAnnotationsQueryCtrl {}\nTimelyAnnotationsQueryCtrl.templateUrl = 'components/annotations.editor.html';\n\nexport {\n TimelyDatasource as Datasource,\n TimelyQueryCtrl as QueryCtrl,\n DatasourceConfigCtrl as ConfigCtrl,\n TimelyAnnotationsQueryCtrl as AnnotationsQueryCtrl\n};\n"]}
|
||||
@@ -0,0 +1,66 @@
|
||||
<div class="gf-form-group">
|
||||
|
||||
<div class="gf-form-group">
|
||||
<h3 class="page-heading">Server Settings</h3>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-7">Hostname</span>
|
||||
<input class="gf-form-input" type="text"
|
||||
ng-model="ctrl.current.jsonData.timelyHost" placeholder="{{ctrl.current.jsonData.suggestHost}}"
|
||||
min-length="0"
|
||||
ng-pattern="/(\w+:{0,1}\w*@)?(\S+)?$/" required></input>
|
||||
<info-popover mode="right-absolute">
|
||||
<p>Hostname of timely server</p>
|
||||
</info-popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-7">HTTPS Port</span>
|
||||
<input class="gf-form-input" type="text"
|
||||
ng-model="ctrl.current.jsonData.httpsPort"
|
||||
min-length="0"
|
||||
ng-pattern="/[0-9]+/" required></input>
|
||||
<info-popover mode="right-absolute">
|
||||
<p>Port number for Timely HTTPS access</p>
|
||||
</info-popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-7">WS Port</span>
|
||||
<input class="gf-form-input" type="text"
|
||||
ng-model="ctrl.current.jsonData.wsPort"
|
||||
min-length="0"
|
||||
ng-pattern="/[0-9]+/"></input>
|
||||
<info-popover mode="right-absolute">
|
||||
<p>Port number for Timely WebSocket access</p>
|
||||
</info-popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<gf-form-switch class="gf-form "
|
||||
label="Basic Auths"
|
||||
checked="ctrl.current.jsonData.basicAuths" switch-class="max-width-6">
|
||||
</gf-form-switch>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">
|
||||
<a ng-href="https://{{ctrl.current.jsonData.timelyHost}}:{{ctrl.current.jsonData.httpsPort}}/version" target="_blank">
|
||||
Browser Cert Check
|
||||
</a>
|
||||
|
||||
<info-popover mode="right">
|
||||
<p>Opens a new browser to test https access, allows you to accept a cert, etc.</p>
|
||||
</info-popover>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"type": "datasource",
|
||||
"name": "Timely",
|
||||
"id": "timely-datasource",
|
||||
|
||||
"metrics": true,
|
||||
"defaultMatchFormat": "pipe",
|
||||
"annotations": true,
|
||||
|
||||
"includes": [
|
||||
{"type": "dashboard", "name": "Timely Standalone Test", "path": "dashboards/standalone_test.json"},
|
||||
{"type": "dashboard", "name": "Accumulo Overview", "path": "dashboards/AccumuloOverview.json"},
|
||||
{"type": "dashboard", "name": "Hadoop Overview", "path": "dashboards/HadoopOverview.json"},
|
||||
{"type": "dashboard", "name": "System Overview", "path": "dashboards/SystemOverview.json"},
|
||||
{"type": "dashboard", "name": "Timely Status", "path": "dashboards/TimelyStatus.json"}
|
||||
],
|
||||
|
||||
"info": {
|
||||
"author": {
|
||||
"name": "National Security Agency",
|
||||
"url": "http://github.com/NationalSecurityAgency/timely"
|
||||
},
|
||||
"logos": {
|
||||
"small": "../timely-app/img/brand_small.png",
|
||||
"large": "../timely-app/img/brand_large.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
'use strict';
|
||||
|
||||
System.register(['lodash', 'app/plugins/sdk'], function (_export, _context) {
|
||||
"use strict";
|
||||
|
||||
var _, QueryCtrl, _createClass, TimelyQueryCtrl;
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
function _possibleConstructorReturn(self, call) {
|
||||
if (!self) {
|
||||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
||||
}
|
||||
|
||||
return call && (typeof call === "object" || typeof call === "function") ? call : self;
|
||||
}
|
||||
|
||||
function _inherits(subClass, superClass) {
|
||||
if (typeof superClass !== "function" && superClass !== null) {
|
||||
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
|
||||
}
|
||||
|
||||
subClass.prototype = Object.create(superClass && superClass.prototype, {
|
||||
constructor: {
|
||||
value: subClass,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
configurable: true
|
||||
}
|
||||
});
|
||||
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [function (_lodash) {
|
||||
_ = _lodash.default;
|
||||
}, function (_appPluginsSdk) {
|
||||
QueryCtrl = _appPluginsSdk.QueryCtrl;
|
||||
}],
|
||||
execute: function () {
|
||||
_createClass = function () {
|
||||
function defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return function (Constructor, protoProps, staticProps) {
|
||||
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
};
|
||||
}();
|
||||
|
||||
_export('TimelyQueryCtrl', TimelyQueryCtrl = function (_QueryCtrl) {
|
||||
_inherits(TimelyQueryCtrl, _QueryCtrl);
|
||||
|
||||
function TimelyQueryCtrl($scope, $injector, uiSegmentSrv) {
|
||||
_classCallCheck(this, TimelyQueryCtrl);
|
||||
|
||||
var _this = _possibleConstructorReturn(this, (TimelyQueryCtrl.__proto__ || Object.getPrototypeOf(TimelyQueryCtrl)).call(this, $scope, $injector));
|
||||
|
||||
_this.errors = _this.validateTarget();
|
||||
_this.aggregators = ['avg', 'sum', 'min', 'max', 'dev', 'zimsum', 'mimmin', 'mimmax'];
|
||||
_this.fillPolicies = ['none', 'nan', 'null', 'zero'];
|
||||
_this.filterTypes = ['wildcard', 'iliteral_or', 'not_iliteral_or', 'not_literal_or', 'iwildcard', 'literal_or', 'regexp'];
|
||||
|
||||
if (!_this.target.aggregator) {
|
||||
_this.target.aggregator = 'sum';
|
||||
}
|
||||
|
||||
if (!_this.target.downsampleAggregator) {
|
||||
_this.target.downsampleAggregator = 'avg';
|
||||
}
|
||||
|
||||
if (!_this.target.downsampleFillPolicy) {
|
||||
_this.target.downsampleFillPolicy = 'none';
|
||||
}
|
||||
|
||||
_this.datasource.getAggregators().then(function (aggs) {
|
||||
if (aggs.length !== 0) {
|
||||
_this.aggregators = aggs;
|
||||
}
|
||||
});
|
||||
|
||||
_this.datasource.getFilterTypes().then(function (filterTypes) {
|
||||
if (filterTypes.length !== 0) {
|
||||
_this.filterTypes = filterTypes;
|
||||
}
|
||||
});
|
||||
|
||||
// needs to be defined here as it is called from typeahead
|
||||
_this.suggestMetrics = function (query, callback) {
|
||||
_this.datasource.metricFindQuery('metrics(' + query + ')').then(_this.getTextValues).then(callback);
|
||||
};
|
||||
|
||||
_this.suggestTagKeys = function (query, callback) {
|
||||
_this.datasource.suggestTagKeys(_this.target.metric).then(callback);
|
||||
};
|
||||
|
||||
_this.suggestTagValues = function (query, callback) {
|
||||
_this.datasource.metricFindQuery('suggest_tagv(' + query + ')').then(_this.getTextValues).then(callback);
|
||||
};
|
||||
|
||||
return _this;
|
||||
}
|
||||
|
||||
_createClass(TimelyQueryCtrl, [{
|
||||
key: 'targetBlur',
|
||||
value: function targetBlur() {
|
||||
this.errors = this.validateTarget();
|
||||
this.refresh();
|
||||
}
|
||||
}, {
|
||||
key: 'getTextValues',
|
||||
value: function getTextValues(metricFindResult) {
|
||||
return _.map(metricFindResult, function (value) {
|
||||
return value.text;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: 'addTag',
|
||||
value: function addTag() {
|
||||
|
||||
if (this.target.filters && this.target.filters.length > 0) {
|
||||
this.errors.tags = "Please remove filters to use tags, tags and filters are mutually exclusive.";
|
||||
}
|
||||
|
||||
if (!this.addTagMode) {
|
||||
this.addTagMode = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.target.tags) {
|
||||
this.target.tags = {};
|
||||
}
|
||||
|
||||
this.errors = this.validateTarget();
|
||||
|
||||
if (!this.errors.tags) {
|
||||
this.target.tags[this.target.currentTagKey] = this.target.currentTagValue;
|
||||
this.target.currentTagKey = '';
|
||||
this.target.currentTagValue = '';
|
||||
this.targetBlur();
|
||||
}
|
||||
|
||||
this.addTagMode = false;
|
||||
}
|
||||
}, {
|
||||
key: 'removeTag',
|
||||
value: function removeTag(key) {
|
||||
delete this.target.tags[key];
|
||||
this.targetBlur();
|
||||
}
|
||||
}, {
|
||||
key: 'editTag',
|
||||
value: function editTag(key, value) {
|
||||
this.removeTag(key);
|
||||
this.target.currentTagKey = key;
|
||||
this.target.currentTagValue = value;
|
||||
this.addTag();
|
||||
}
|
||||
}, {
|
||||
key: 'closeAddTagMode',
|
||||
value: function closeAddTagMode() {
|
||||
this.addTagMode = false;
|
||||
return;
|
||||
}
|
||||
}, {
|
||||
key: 'addFilter',
|
||||
value: function addFilter() {
|
||||
|
||||
if (this.target.tags && _.size(this.target.tags) > 0) {
|
||||
this.errors.filters = "Please remove tags to use filters, tags and filters are mutually exclusive.";
|
||||
}
|
||||
|
||||
if (!this.addFilterMode) {
|
||||
this.addFilterMode = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.target.filters) {
|
||||
this.target.filters = [];
|
||||
}
|
||||
|
||||
if (!this.target.currentFilterType) {
|
||||
this.target.currentFilterType = 'iliteral_or';
|
||||
}
|
||||
|
||||
if (!this.target.currentFilterGroupBy) {
|
||||
this.target.currentFilterGroupBy = false;
|
||||
}
|
||||
|
||||
this.errors = this.validateTarget();
|
||||
|
||||
if (!this.errors.filters) {
|
||||
var currentFilter = {
|
||||
type: this.target.currentFilterType,
|
||||
tagk: this.target.currentFilterKey,
|
||||
filter: this.target.currentFilterValue,
|
||||
groupBy: this.target.currentFilterGroupBy
|
||||
};
|
||||
this.target.filters.push(currentFilter);
|
||||
this.target.currentFilterType = 'literal_or';
|
||||
this.target.currentFilterKey = '';
|
||||
this.target.currentFilterValue = '';
|
||||
this.target.currentFilterGroupBy = false;
|
||||
this.targetBlur();
|
||||
}
|
||||
|
||||
this.addFilterMode = false;
|
||||
}
|
||||
}, {
|
||||
key: 'removeFilter',
|
||||
value: function removeFilter(index) {
|
||||
this.target.filters.splice(index, 1);
|
||||
this.targetBlur();
|
||||
}
|
||||
}, {
|
||||
key: 'editFilter',
|
||||
value: function editFilter(fil, index) {
|
||||
this.removeFilter(index);
|
||||
this.target.currentFilterKey = fil.tagk;
|
||||
this.target.currentFilterValue = fil.filter;
|
||||
this.target.currentFilterType = fil.type;
|
||||
this.target.currentFilterGroupBy = fil.groupBy;
|
||||
this.addFilter();
|
||||
}
|
||||
}, {
|
||||
key: 'closeAddFilterMode',
|
||||
value: function closeAddFilterMode() {
|
||||
this.addFilterMode = false;
|
||||
return;
|
||||
}
|
||||
}, {
|
||||
key: 'validateTarget',
|
||||
value: function validateTarget() {
|
||||
var errs = {};
|
||||
|
||||
if (this.target.shouldDownsample) {
|
||||
try {
|
||||
if (this.target.downsampleInterval) {
|
||||
kbn.describe_interval(this.target.downsampleInterval);
|
||||
} else {
|
||||
errs.downsampleInterval = "You must supply a downsample interval (e.g. '1m' or '1h').";
|
||||
}
|
||||
} catch (err) {
|
||||
errs.downsampleInterval = err.message;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.target.tags && _.has(this.target.tags, this.target.currentTagKey)) {
|
||||
errs.tags = "Duplicate tag key '" + this.target.currentTagKey + "'.";
|
||||
}
|
||||
|
||||
return errs;
|
||||
}
|
||||
}]);
|
||||
|
||||
return TimelyQueryCtrl;
|
||||
}(QueryCtrl));
|
||||
|
||||
_export('TimelyQueryCtrl', TimelyQueryCtrl);
|
||||
|
||||
TimelyQueryCtrl.templateUrl = 'components/query.editor.html';
|
||||
}
|
||||
};
|
||||
});
|
||||
//# sourceMappingURL=query_ctrl.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/datasource/query_ctrl.js"],"names":["_","QueryCtrl","TimelyQueryCtrl","$scope","$injector","uiSegmentSrv","errors","validateTarget","aggregators","fillPolicies","filterTypes","target","aggregator","downsampleAggregator","downsampleFillPolicy","datasource","getAggregators","then","aggs","length","getFilterTypes","suggestMetrics","query","callback","metricFindQuery","getTextValues","suggestTagKeys","metric","suggestTagValues","refresh","metricFindResult","map","value","text","filters","tags","addTagMode","currentTagKey","currentTagValue","targetBlur","key","removeTag","addTag","size","addFilterMode","currentFilterType","currentFilterGroupBy","currentFilter","type","tagk","currentFilterKey","filter","currentFilterValue","groupBy","push","index","splice","fil","removeFilter","addFilter","errs","shouldDownsample","downsampleInterval","kbn","describe_interval","err","message","has","templateUrl"],"mappings":"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 truncated
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
System.register(['./components/config', './components/metrics', './components/authspage'], function (_export, _context) {
|
||||
"use strict";
|
||||
|
||||
var TimelyAppConfigCtrl, MetricsPageCtrl, AuthsPageCtrl;
|
||||
return {
|
||||
setters: [function (_componentsConfig) {
|
||||
TimelyAppConfigCtrl = _componentsConfig.TimelyAppConfigCtrl;
|
||||
}, function (_componentsMetrics) {
|
||||
MetricsPageCtrl = _componentsMetrics.MetricsPageCtrl;
|
||||
}, function (_componentsAuthspage) {
|
||||
AuthsPageCtrl = _componentsAuthspage.AuthsPageCtrl;
|
||||
}],
|
||||
execute: function () {
|
||||
_export('ConfigCtrl', TimelyAppConfigCtrl);
|
||||
|
||||
_export('MetricsPageCtrl', MetricsPageCtrl);
|
||||
|
||||
_export('AuthsPageCtrl', AuthsPageCtrl);
|
||||
}
|
||||
};
|
||||
});
|
||||
//# sourceMappingURL=module.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../src/module.js"],"names":["TimelyAppConfigCtrl","MetricsPageCtrl","AuthsPageCtrl"],"mappings":";;;;;;;;AAAQA,yB,qBAAAA,mB;;AACAC,qB,sBAAAA,e;;AACAC,mB,wBAAAA,a;;;4BAGNF,mB;;iCACAC,e;;+BACAC,a","file":"module.js","sourcesContent":["import {TimelyAppConfigCtrl} from './components/config';\nimport {MetricsPageCtrl} from './components/metrics';\nimport {AuthsPageCtrl} from './components/authspage';\n\nexport {\n TimelyAppConfigCtrl as ConfigCtrl,\n MetricsPageCtrl,\n AuthsPageCtrl\n};\n"]}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"type": "app",
|
||||
"name": "Timely",
|
||||
"id": "timely-app",
|
||||
|
||||
"css": {
|
||||
"dark": "css/dark.css",
|
||||
"light": "css/light.css"
|
||||
},
|
||||
|
||||
"info": {
|
||||
"description": "Timely App",
|
||||
"author": {
|
||||
"name": "National Security Agency",
|
||||
"url": "http://github.com/NationalSecurityAgency"
|
||||
},
|
||||
"keywords": ["timely app datasource"],
|
||||
"logos": {
|
||||
"small": "img/brand_small.png",
|
||||
"large": "img/brand_large.png"
|
||||
},
|
||||
"links": [
|
||||
{"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"},
|
||||
{"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"}
|
||||
],
|
||||
"version": "0.0.3",
|
||||
"updated": "2016-06-30"
|
||||
},
|
||||
|
||||
"includes": [
|
||||
{"type": "datasource", "name": "Timely"},
|
||||
{ "type": "page", "name": "Login", "component": "AuthsPageCtrl", "role": "Viewer", "addToNav": true},
|
||||
{ "type": "page", "name": "Metrics", "component": "MetricsPageCtrl", "role": "Viewer", "addToNav": true}
|
||||
],
|
||||
|
||||
"dependencies": {
|
||||
"grafanaVersion": "3.x.x",
|
||||
"plugins": []
|
||||
}
|
||||
|
||||
}
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "timely-app",
|
||||
"private": true,
|
||||
"version": "0.0.2",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/NationalSecurityAgency/timely.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/NationalSecurityAgency/timely/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel": "~6.5.1",
|
||||
"babel-plugin-transform-es2015-for-of": "^6.8.0",
|
||||
"grunt": "~0.4.5",
|
||||
"grunt-babel": "~6.0.0",
|
||||
"grunt-contrib-clean": "~0.6.0",
|
||||
"grunt-contrib-copy": "~0.8.2",
|
||||
"grunt-contrib-uglify": "~0.11.0",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"grunt-execute": "~0.2.2",
|
||||
"grunt-mocha-test": "~0.12.7",
|
||||
"grunt-systemjs-builder": "^0.2.5",
|
||||
"load-grunt-tasks": "~3.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ag-grid": "^5.0.2",
|
||||
"babel-plugin-transform-es2015-modules-systemjs": "^6.5.0",
|
||||
"babel-preset-es2015": "^6.5.0",
|
||||
"dynatable": "alfajango/jquery-dynatable#v0.3.1",
|
||||
"lodash": "~4.0.0"
|
||||
},
|
||||
"homepage": "https://github.com/NationalSecurityAgency/timely"
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"type": "app",
|
||||
"name": "Timely",
|
||||
"id": "timely-app",
|
||||
|
||||
"css": {
|
||||
"dark": "css/dark.css",
|
||||
"light": "css/light.css"
|
||||
},
|
||||
|
||||
"info": {
|
||||
"description": "Timely App",
|
||||
"author": {
|
||||
"name": "National Security Agency",
|
||||
"url": "http://github.com/NationalSecurityAgency"
|
||||
},
|
||||
"keywords": ["timely app datasource"],
|
||||
"logos": {
|
||||
"small": "img/brand_small.png",
|
||||
"large": "img/brand_large.png"
|
||||
},
|
||||
"links": [
|
||||
{"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"},
|
||||
{"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"}
|
||||
],
|
||||
"version": "0.0.3",
|
||||
"updated": "2016-06-30"
|
||||
},
|
||||
|
||||
"includes": [
|
||||
{"type": "datasource", "name": "Timely"},
|
||||
{ "type": "page", "name": "Login", "component": "AuthsPageCtrl", "role": "Viewer", "addToNav": true},
|
||||
{ "type": "page", "name": "Metrics", "component": "MetricsPageCtrl", "role": "Viewer", "addToNav": true}
|
||||
],
|
||||
|
||||
"dependencies": {
|
||||
"grafanaVersion": "3.x.x",
|
||||
"plugins": []
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-13">OpenTSDB metrics query</span>
|
||||
<input type="text" class="gf-form-input" ng-model='ctrl.annotation.target' placeholder="events.eventname"></input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-13">Show Global Annotations?</span>
|
||||
<editor-checkbox text="" model="ctrl.annotation.isGlobal"></editor-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Timely Login</h1>
|
||||
</div>
|
||||
|
||||
<div ng-if="ctrl.datasources.length === 0">
|
||||
<h4>No Timely datasources are defined.</h4>
|
||||
<i><a href="datasources/new">Create one</a></i>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="source in ctrl.datasources" ng-init="d=source" ng-controller="AuthsCtrl as actrl">
|
||||
<div class="gf-box">
|
||||
<div class="gf-box-header p-x-1 p-y-1">
|
||||
<h3 class="">{{d.name}}
|
||||
<span class="pull-right">
|
||||
<h6><i>https://{{d.jsonData.timelyHost}}:{{d.jsonData.httpsPort}}/login</i></h6>
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="gf-box-body">
|
||||
<div ng-if="d.jsonData.basicAuths" class="p-b-1">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-7">
|
||||
User
|
||||
</span>
|
||||
<input id="timlely-user" class="gf-form-input max-width-21" type="text" ng-model='actrl.basicAuthUser' placeholder="user" required></input>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-7">
|
||||
Password
|
||||
</span>
|
||||
<input id="timely-password" class="gf-form-input max-width-21" type="password" ng-model='actrl.basicAuthPassword' placeholder="password" required></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="actrl.fetching" class="gf-row">
|
||||
<div class="alert-{{actrl.fetching.status}} alert">
|
||||
<div class="alert-title">{{actrl.fetching.title}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-button-row center-block">
|
||||
<button type="submit" class="btn btn-success" ng-click="actrl.login(d)">
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,97 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
export class AuthsCtrl {
|
||||
|
||||
constructor(backendSrv, $rootScope, $timeout){
|
||||
this.$rootScope = $rootScope;
|
||||
this.$timeout = $timeout;
|
||||
this.backendSrv = backendSrv;
|
||||
}
|
||||
|
||||
login(datasource){
|
||||
this.fetching = {};
|
||||
this.fetching.done = false;
|
||||
this.fetching.show = true;
|
||||
this.fetching.status = 'warning';
|
||||
this.fetching.title = 'Fetching auths...';
|
||||
|
||||
//this.$rootScope.appEvent('alert-success', ['Authorization Cookie Received', '']);
|
||||
//this.$rootScope.appEvent('alert-error', ['Authorization Failed', '']);
|
||||
//this.$rootScope.appEvent('alert-warning', ['Fetching Authorizations', '']);
|
||||
|
||||
var uri = this._getURI(datasource.jsonData);
|
||||
|
||||
if(datasource.jsonData.basicAuths){
|
||||
if( this.basicAuthUser && this.basicAuthPassword){
|
||||
var authBlob = {username: this.basicAuthUser, password: this.basicAuthPassword};
|
||||
this.doPostLogin(uri, authBlob);
|
||||
} else {
|
||||
this.$rootScope.appEvent('alert-warning', [datasource.name, 'requires User/Password']);
|
||||
this.fetching.status = 'warning';
|
||||
this.fetching.title = 'User/Password required';
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.doGetLogin(uri);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
doPostLogin(uri, authBlob){
|
||||
this.doXHR('POST', uri, authBlob);
|
||||
}
|
||||
|
||||
doGetLogin(uri){
|
||||
this.doXHR('GET', uri)
|
||||
}
|
||||
|
||||
doXHR(verb, uri, data){
|
||||
if( !data){
|
||||
data = "";
|
||||
}
|
||||
var r = new XMLHttpRequest();
|
||||
r.open(verb, uri, true);
|
||||
r.withCredentials = true;
|
||||
r.setRequestHeader('Content-Type', 'application/json');
|
||||
r.onreadystatechange = function () {
|
||||
this.handleLoginResult(r);
|
||||
}.bind(this);
|
||||
r.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
handleLoginResult(response){
|
||||
// wait for readyState to be '4 => done'
|
||||
if (response.readyState != 4) return;
|
||||
|
||||
this.fetching.done = true;
|
||||
|
||||
switch(response.status){
|
||||
case 200:
|
||||
var cookie = (document.cookie.match(/^(?:.*;)?TSESSIONID=([^;]+)(?:.*)?$/)||[,null])[1]
|
||||
console.log(cookie);
|
||||
console.log(document.cookie);
|
||||
|
||||
this.fetching.status = 'success';
|
||||
this.fetching.title = 'Login successful';
|
||||
this.$rootScope.appEvent('alert-success', ['Success', '']);
|
||||
break;
|
||||
default:
|
||||
this.fetching.status = 'warning';
|
||||
this.fetching.title = response.statusText;
|
||||
this.$rootScope.appEvent('alert-warning', [response.status+': '+response.statusText,'']);
|
||||
break;
|
||||
}
|
||||
|
||||
if (r.readyState != 4 || r.status != 200) return;
|
||||
alert("Success: " + r.getResponseHeader('Set-Cookie'));
|
||||
}
|
||||
|
||||
_getURI(jsonData){
|
||||
return 'https://'+jsonData.timelyHost+':'+jsonData.httpsPort+'/login'
|
||||
}
|
||||
|
||||
_showResults(){
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
import _ from 'lodash';
|
||||
import {coreModule, appEvents} from 'app/core/core';
|
||||
import {AuthsCtrl} from 'plugins/timely-app/components/auths';
|
||||
|
||||
export class AuthsPageCtrl {
|
||||
|
||||
constructor(backendSrv, datasourceSrv){
|
||||
this.datasourceSrv = datasourceSrv;
|
||||
this.datasources = this.getTimelyDatasources();
|
||||
coreModule.controller('AuthsCtrl', AuthsCtrl);
|
||||
}
|
||||
|
||||
getTimelyDatasources(){
|
||||
var allSources = this.datasourceSrv.getAll();
|
||||
|
||||
var sources = _.map(allSources, function(value, key, col){
|
||||
var ret = {};
|
||||
ret.meta = {};
|
||||
ret.meta.type = col[key].meta.type;
|
||||
ret.meta.id = col[key].meta.id;
|
||||
ret.name = key;
|
||||
ret.jsonData = col[key].jsonData;
|
||||
return ret;
|
||||
});
|
||||
return _.filter(sources, function(d){
|
||||
return (d.meta.type === 'datasource' && d.meta.id === 'timely-datasource');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
AuthsPageCtrl.templateUrl = 'components/auths.html';
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
<h3 class="page-heading">Timely App Configuration</h3>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">Currently, all configuration is done in the Timely datasource.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
export class TimelyAppConfigCtrl {
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
TimelyAppConfigCtrl.templateUrl = 'components/config.html';
|
||||
@@ -0,0 +1,53 @@
|
||||
<div class="page-header">
|
||||
<h1>Metrics Browser</h1>
|
||||
</div>
|
||||
|
||||
<div ng-hide="ctrl.hasDatasource" >
|
||||
<h4>No Timely datasources are defined.</h4>
|
||||
<i><a href="datasources/new">Create one</a></i>
|
||||
</div>
|
||||
|
||||
<div class="gf-box" ng-show="ctrl.hasDatasource">
|
||||
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-label width-7 p-y-1 p-x-1">Datasource</span>
|
||||
<div class="gf-form-select-wrapper gf-form-select-wrapper--has-help-icon max-width-24">
|
||||
<select class="gf-form-input"
|
||||
ng-model="ctrl.source"
|
||||
ng-options="d.name for d in ctrl.datasources"
|
||||
ng-change=ctrl.updateDatasource()></select>
|
||||
<info-popover mode="right-absolute">
|
||||
Select a Datasource<br>
|
||||
</info-popover>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-box-body">
|
||||
<div class="grafana-options-table">
|
||||
<div class="gf-form width-20 pull-right">
|
||||
<input type="text" class="gf-form-input" placeholder="filter" ng-model="searchText">
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Metric</th>
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
<tr ng-repeat="metric in ctrl.metrics | filter:searchText">
|
||||
<td>{{metric.metric}}</td>
|
||||
<td class="p-x-1" ng-repeat="tag in metric.tags">
|
||||
{{tag.name}} = {{tag.values}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
import _ from 'lodash';
|
||||
import {AuthsPageCtrl} from 'plugins/timely-app/components/authspage';
|
||||
|
||||
export class MetricsPageCtrl {
|
||||
|
||||
constructor(backendSrv, datasourceSrv){
|
||||
this.hasDatasource = false;
|
||||
this.backendSrv = backendSrv;
|
||||
var apc = new AuthsPageCtrl(backendSrv, datasourceSrv);
|
||||
this.datasources = apc.getTimelyDatasources();
|
||||
if( this.datasources.length > 0){
|
||||
this.source = this.datasources[0];
|
||||
this.updateDatasource();
|
||||
}
|
||||
}
|
||||
|
||||
updateDatasource(){
|
||||
this._clearMetrics();
|
||||
var uri = this._getURI(this.source.jsonData);
|
||||
this.backendSrv.request(
|
||||
{ method: 'GET',
|
||||
url: uri,
|
||||
withCredentials: true,
|
||||
params: "" }
|
||||
).then(
|
||||
response => {
|
||||
this.metrics = this._transformMetrics(response.metrics);
|
||||
this.hasDatasource = true;
|
||||
});
|
||||
}
|
||||
|
||||
_clearMetrics(){
|
||||
this.metrics = [];
|
||||
}
|
||||
|
||||
_transformMetrics(metrics){
|
||||
var ret = [];
|
||||
_.each(metrics, function(value, idx){
|
||||
var metric = {};
|
||||
metric.metric = value.metric;
|
||||
metric.tags = [];
|
||||
_.map(value.tags, function(tag, key){
|
||||
// result || (result = {});
|
||||
// result['name'] = tag.key;
|
||||
// result['values'] || (result['values'] = []);
|
||||
// result['values'].push(tag.value);
|
||||
var gIdx = _.findIndex(metric.tags, {'name': tag.key});
|
||||
var grouped;
|
||||
if( gIdx == -1 ){
|
||||
grouped = {name: '', values: []};
|
||||
} else {
|
||||
grouped = metric.tags[gIdx];
|
||||
}
|
||||
grouped.values.push(tag.value);
|
||||
grouped.values = _.sortBy(_.uniq(grouped.values))
|
||||
grouped.name = tag.key;
|
||||
if( gIdx > -1 ){
|
||||
metric.tags[gIdx] = grouped;
|
||||
} else {
|
||||
metric.tags.push(grouped);
|
||||
}
|
||||
}.bind(metric));
|
||||
metric.tags = _.sortBy(metric.tags, 'name')
|
||||
ret.push(metric);
|
||||
}.bind(ret));
|
||||
|
||||
return _.sortBy(ret, 'metric');
|
||||
}
|
||||
|
||||
_getURI(jsonData){
|
||||
return 'https://'+jsonData.timelyHost+':'+jsonData.httpsPort+'/api/metrics'
|
||||
}
|
||||
|
||||
}
|
||||
MetricsPageCtrl.templateUrl = 'components/metrics.html';
|
||||
@@ -0,0 +1,257 @@
|
||||
<query-editor-row query-ctrl="ctrl" can-collapse="false">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-25">
|
||||
<label class="gf-form-label query-keyword width-8">
|
||||
Metric
|
||||
<label class="gf-form-label" bs-tooltip="ctrl.errors.metric" style="color: rgb(229, 189, 28)" ng-show="ctrl.errors.metric">
|
||||
<i class="fa fa-warning"></i>
|
||||
</label>
|
||||
</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.target.metric"
|
||||
spellcheck='false' bs-typeahead="ctrl.suggestMetrics" placeholder="metric name" data-min-length=0 data-items=100
|
||||
ng-blur="ctrl.targetBlur()">
|
||||
</input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword">
|
||||
Aggregator
|
||||
<a bs-tooltip="ctrl.errors.aggregator" style="color: rgb(229, 189, 28)" ng-show="ctrl.errors.aggregator">
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</label>
|
||||
<div class="gf-form-select-wrapper max-width-15">
|
||||
<select ng-model="ctrl.target.aggregator" class="gf-form-input"
|
||||
ng-options="agg for agg in ctrl.aggregators"
|
||||
ng-change="ctrl.targetBlur()">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form max-width-20">
|
||||
<label class="gf-form-label query-keyword width-6">
|
||||
Alias:
|
||||
<info-popover mode="right-normal">
|
||||
Use patterns like $tag_tagname to replace part of the alias for a tag value
|
||||
</info-popover>
|
||||
</label>
|
||||
<input type="text" class="gf-form-input"
|
||||
ng-model="ctrl.target.alias"
|
||||
spellcheck='false'
|
||||
placeholder="series alias"
|
||||
data-min-length=0 data-items=100
|
||||
ng-blur="ctrl.targetBlur()"></input>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-25">
|
||||
<label class="gf-form-label query-keyword width-8">Down sample</label>
|
||||
<input type="text" class="gf-form-input"
|
||||
ng-model="ctrl.target.downsampleInterval"
|
||||
ng-model-onblur
|
||||
ng-change="ctrl.targetBlur()"
|
||||
placeholder="interval"></input>
|
||||
<info-popover mode="right-absolute">
|
||||
blank for auto, or for example <code>1m</code>
|
||||
</info-popover>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword">Aggregator</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select ng-model="ctrl.target.downsampleAggregator" class="gf-form-input"
|
||||
ng-options="agg for agg in ctrl.aggregators"
|
||||
ng-change="ctrl.targetBlur()">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-if="ctrl.tsdbVersion >= 2">
|
||||
<label class="gf-form-label query-keyword width-6">Fill</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select ng-model="ctrl.target.downsampleFillPolicy" class="gf-form-input"
|
||||
ng-options="agg for agg in ctrl.fillPolicies"
|
||||
ng-change="ctrl.targetBlur()">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<gf-form-switch class="gf-form"
|
||||
label="Disable downsampling"
|
||||
label-class="query-keyword"
|
||||
checked="ctrl.target.disableDownsampling"
|
||||
on-change="ctrl.targetBlur()">
|
||||
</gf-form-switch>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline" ng-if="ctrl.tsdbVersion >= 2">
|
||||
<div class="gf-form">
|
||||
|
||||
<label class="gf-form-label query-keyword width-8">
|
||||
Filters
|
||||
<info-popover mode="right-normal">
|
||||
Filters does not work with tags, either of the two will work but not both.
|
||||
</info-popover>
|
||||
</label>
|
||||
|
||||
<div ng-repeat="fil in ctrl.target.filters track by $index" class="gf-form-label">
|
||||
{{fil.tagk}} = {{fil.type}}({{fil.filter}}) , groupBy = {{fil.groupBy}}
|
||||
<a ng-click="ctrl.editFilter(fil, $index)">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a ng-click="ctrl.removeFilter($index)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</div>
|
||||
<label class="gf-form-label query-keyword" ng-hide="ctrl.addFilterMode">
|
||||
<a ng-click="ctrl.addFilter()">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline" ng-show="ctrl.addFilterMode">
|
||||
<div class="gf-form">
|
||||
<input type="text" class="gf-form-input" spellcheck='false'
|
||||
bs-typeahead="ctrl.suggestTagKeys" data-min-length=0 data-items=100
|
||||
ng-model="ctrl.target.currentFilterKey" placeholder="key">
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword">Type</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select ng-model="ctrl.target.currentFilterType" class="gf-form-input" ng-options="filType for filType in ctrl.filterTypes">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<input type="text" class="gf-form-input" spellcheck='false' bs-typeahead="ctrl.suggestTagValues" data-min-length=0 data-items=100 ng-model="ctrl.target.currentFilterValue" placeholder="filter">
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<gf-form-switch class="gf-form"
|
||||
label="Group by"
|
||||
label-class="query-keyword"
|
||||
checked="ctrl.target.currentFilterGroupBy"
|
||||
on-change="ctrl.targetBlur()">
|
||||
</gf-form-switch>
|
||||
|
||||
<div class="gf-form" ng-show="ctrl.addFilterMode">
|
||||
<label class="gf-form-label" ng-show="ctrl.errors.filters">
|
||||
<a bs-tooltip="ctrl.errors.filters" style="color: rgb(229, 189, 28)" >
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</label>
|
||||
<label class="gf-form-label">
|
||||
<a ng-click="ctrl.addFilter()" ng-hide="ctrl.errors.filters">add filter</a>
|
||||
<a ng-click="ctrl.closeAddFilterMode()">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-8">
|
||||
Tags
|
||||
<info-popover mode="right-normal" ng-if="ctrl.tsdbVersion >= 2">
|
||||
Please use filters, tags are deprecated in opentsdb 2.2
|
||||
</info-popover>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-repeat="(key, value) in ctrl.target.tags track by $index" class="gf-form">
|
||||
<label class="gf-form-label">
|
||||
{{key}} = {{value}}
|
||||
<a ng-click="ctrl.editTag(key, value)">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a ng-click="ctrl.removeTag(key)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-hide="ctrl.addTagMode">
|
||||
<label class="gf-form-label query-keyword">
|
||||
<a ng-click="ctrl.addTag()"><i class="fa fa-plus"></i></a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-show="ctrl.addTagMode">
|
||||
<input type="text"
|
||||
class="gf-form-input" spellcheck='false'
|
||||
bs-typeahead="ctrl.suggestTagKeys" data-min-length=0 data-items=100
|
||||
ng-model="ctrl.target.currentTagKey" placeholder="key">
|
||||
</input>
|
||||
|
||||
<input type="text" class="gf-form-input"
|
||||
spellcheck='false' bs-typeahead="ctrl.suggestTagValues"
|
||||
data-min-length=0 data-items=100 ng-model="ctrl.target.currentTagValue" placeholder="value">
|
||||
</input>
|
||||
|
||||
<label class="gf-form-label" ng-show="ctrl.errors.tags">
|
||||
<a bs-tooltip="ctrl.errors.tags" style="color: rgb(229, 189, 28)" >
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</label>
|
||||
<label class="gf-form-label" >
|
||||
<a ng-click="ctrl.addTag()" ng-hide="ctrl.errors.tags">add tag</a>
|
||||
<a ng-click="ctrl.closeAddTagMode()"><i class="fa fa-remove"></i></a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<gf-form-switch class="gf-form" label="Rate" label-class="width-8 query-keyword" checked="ctrl.target.shouldComputeRate" on-change="ctrl.targetBlur()">
|
||||
</gf-form-switch>
|
||||
|
||||
<gf-form-switch ng-hide="!ctrl.target.shouldComputeRate"
|
||||
class="gf-form" label="Counter" label-class="query-keyword" checked="ctrl.target.isCounter" on-change="ctrl.targetBlur()">
|
||||
</gf-form-switch>
|
||||
|
||||
|
||||
<div class="gf-form" ng-hide="!ctrl.target.isCounter || !ctrl.target.shouldComputeRate">
|
||||
<label class="gf-form-label query-keyword">Counter Max</label>
|
||||
<input type="text" class="gf-form-input"
|
||||
ng-disabled="!ctrl.target.shouldComputeRate"
|
||||
ng-model="ctrl.target.counterMax" spellcheck='false'
|
||||
placeholder="max value" ng-model-onblur
|
||||
ng-blur="ctrl.targetBlur()">
|
||||
</input>
|
||||
|
||||
<label class="gf-form-label query-keyword">Reset Value</label>
|
||||
<input type="text" class="tight-form-input input-small"
|
||||
ng-disabled="!ctrl.target.shouldComputeRate"
|
||||
ng-model="ctrl.target.counterResetValue" spellcheck='false'
|
||||
placeholder="reset value" ng-model-onblur
|
||||
ng-blur="ctrl.targetBlur()">
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
</query-editor-row>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.timely-app-heading {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
|
||||
export class DatasourceConfigCtrl {
|
||||
|
||||
constructor() {
|
||||
this.current.jsonData = this.current.jsonData || {};
|
||||
this.current.access = 'direct';
|
||||
this.current.basicAuth = false;
|
||||
this.current.withCredentials = true;
|
||||
this.current.suggestHost = 'localhost';
|
||||
}
|
||||
}
|
||||
DatasourceConfigCtrl.templateUrl = 'datasource/partials/config.html';
|
||||
@@ -0,0 +1,786 @@
|
||||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_TIMELY",
|
||||
"label": "Timely",
|
||||
"description": "",
|
||||
"type": "datasource",
|
||||
"pluginId": "timely-datasource",
|
||||
"pluginName": "Timely"
|
||||
}
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "singlestat",
|
||||
"name": "Singlestat",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "graph",
|
||||
"name": "Graph",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "3.1.0"
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
"id": "timely-datasource",
|
||||
"name": "Timely",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"id": null,
|
||||
"title": "Timely Status",
|
||||
"tags": [],
|
||||
"style": "dark",
|
||||
"timezone": "browser",
|
||||
"editable": true,
|
||||
"hideControls": false,
|
||||
"sharedCrosshair": false,
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "150",
|
||||
"panels": [
|
||||
{
|
||||
"cacheTimeout": null,
|
||||
"colorBackground": false,
|
||||
"colorValue": false,
|
||||
"colors": [
|
||||
"rgba(245, 54, 54, 0.9)",
|
||||
"rgba(237, 129, 40, 0.89)",
|
||||
"rgba(50, 172, 45, 0.97)"
|
||||
],
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"format": "none",
|
||||
"gauge": {
|
||||
"maxValue": 100000,
|
||||
"minValue": 0,
|
||||
"show": true,
|
||||
"thresholdLabels": false,
|
||||
"thresholdMarkers": false
|
||||
},
|
||||
"height": "150",
|
||||
"hideTimeOverride": true,
|
||||
"id": 8,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
{
|
||||
"name": "value to text",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"name": "range to text",
|
||||
"value": 2
|
||||
}
|
||||
],
|
||||
"maxDataPoints": 100,
|
||||
"nullPointMode": "connected",
|
||||
"nullText": null,
|
||||
"postfix": "",
|
||||
"postfixFontSize": "50%",
|
||||
"prefix": "",
|
||||
"prefixFontSize": "50%",
|
||||
"rangeMaps": [
|
||||
{
|
||||
"from": "null",
|
||||
"text": "N/A",
|
||||
"to": "null"
|
||||
}
|
||||
],
|
||||
"span": 4,
|
||||
"sparkline": {
|
||||
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||
"full": false,
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "count",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "timely.metrics.received",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": "",
|
||||
"timeFrom": null,
|
||||
"timeShift": "1m",
|
||||
"title": "Metrics Received Last Minute",
|
||||
"transparent": true,
|
||||
"type": "singlestat",
|
||||
"valueFontSize": "80%",
|
||||
"valueMaps": [
|
||||
{
|
||||
"op": "=",
|
||||
"text": "N/A",
|
||||
"value": "null"
|
||||
}
|
||||
],
|
||||
"valueName": "avg"
|
||||
},
|
||||
{
|
||||
"cacheTimeout": null,
|
||||
"colorBackground": false,
|
||||
"colorValue": false,
|
||||
"colors": [
|
||||
"rgba(245, 54, 54, 0.9)",
|
||||
"rgba(237, 129, 40, 0.89)",
|
||||
"rgba(50, 172, 45, 0.97)"
|
||||
],
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"format": "none",
|
||||
"gauge": {
|
||||
"maxValue": 100,
|
||||
"minValue": 0,
|
||||
"show": false,
|
||||
"thresholdLabels": false,
|
||||
"thresholdMarkers": true
|
||||
},
|
||||
"height": "150",
|
||||
"hideTimeOverride": true,
|
||||
"id": 9,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
{
|
||||
"name": "value to text",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"name": "range to text",
|
||||
"value": 2
|
||||
}
|
||||
],
|
||||
"maxDataPoints": 100,
|
||||
"nullPointMode": "connected",
|
||||
"nullText": null,
|
||||
"postfix": "",
|
||||
"postfixFontSize": "50%",
|
||||
"prefix": "",
|
||||
"prefixFontSize": "50%",
|
||||
"rangeMaps": [
|
||||
{
|
||||
"from": "null",
|
||||
"text": "N/A",
|
||||
"to": "null"
|
||||
}
|
||||
],
|
||||
"span": 4,
|
||||
"sparkline": {
|
||||
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||
"full": false,
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "count",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "count",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "sys.collectd.queue_length",
|
||||
"refId": "A",
|
||||
"tags": {}
|
||||
}
|
||||
],
|
||||
"thresholds": "",
|
||||
"timeShift": "1m",
|
||||
"title": "CollectD Instances Reporting",
|
||||
"transparent": true,
|
||||
"type": "singlestat",
|
||||
"valueFontSize": "80%",
|
||||
"valueMaps": [
|
||||
{
|
||||
"op": "=",
|
||||
"text": "N/A",
|
||||
"value": "null"
|
||||
}
|
||||
],
|
||||
"valueName": "avg"
|
||||
},
|
||||
{
|
||||
"cacheTimeout": null,
|
||||
"colorBackground": false,
|
||||
"colorValue": false,
|
||||
"colors": [
|
||||
"rgba(245, 54, 54, 0.9)",
|
||||
"rgba(237, 129, 40, 0.89)",
|
||||
"rgba(50, 172, 45, 0.97)"
|
||||
],
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"format": "none",
|
||||
"gauge": {
|
||||
"maxValue": 100,
|
||||
"minValue": 0,
|
||||
"show": false,
|
||||
"thresholdLabels": false,
|
||||
"thresholdMarkers": true
|
||||
},
|
||||
"height": "150",
|
||||
"hideTimeOverride": true,
|
||||
"id": 10,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
{
|
||||
"name": "value to text",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"name": "range to text",
|
||||
"value": 2
|
||||
}
|
||||
],
|
||||
"maxDataPoints": 100,
|
||||
"nullPointMode": "connected",
|
||||
"nullText": null,
|
||||
"postfix": "",
|
||||
"postfixFontSize": "50%",
|
||||
"prefix": "",
|
||||
"prefixFontSize": "50%",
|
||||
"rangeMaps": [
|
||||
{
|
||||
"from": "null",
|
||||
"text": "N/A",
|
||||
"to": "null"
|
||||
}
|
||||
],
|
||||
"span": 4,
|
||||
"sparkline": {
|
||||
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||
"full": false,
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "sys.collectd.derive.dropped",
|
||||
"refId": "A",
|
||||
"tags": {}
|
||||
}
|
||||
],
|
||||
"thresholds": "",
|
||||
"timeShift": "1m",
|
||||
"title": "Metrics Dropped By CollectD",
|
||||
"transparent": true,
|
||||
"type": "singlestat",
|
||||
"valueFontSize": "80%",
|
||||
"valueMaps": [
|
||||
{
|
||||
"op": "=",
|
||||
"text": "N/A",
|
||||
"value": "null"
|
||||
}
|
||||
],
|
||||
"valueName": "avg"
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 3,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "timely.keys.metric.inserted",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Metrics Inserted",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "none",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 4,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "timely.keys.meta.inserted",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Meta Inserted",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "none",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 5,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "timely.metrics.received",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Metrics Received",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "none",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 7,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 6,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "sys.collectd.cache_size",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CollectD Cache Sizes",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 11,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 6,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "sys.collectd.queue_length",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CollectD Queue Length",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
}
|
||||
],
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"now": true,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"refresh": "1m",
|
||||
"schemaVersion": 12,
|
||||
"version": 8,
|
||||
"links": [],
|
||||
"gnetId": null
|
||||
}
|
||||
@@ -0,0 +1,434 @@
|
||||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_NAME",
|
||||
"type": "datasource",
|
||||
"pluginId": "timely-datasource"
|
||||
}
|
||||
],
|
||||
"id": null,
|
||||
"title": "Timely Standalone Demo",
|
||||
"originalTitle": "Timely Standalone Demo",
|
||||
"tags": ["imported"],
|
||||
"style": "dark",
|
||||
"timezone": "browser",
|
||||
"editable": true,
|
||||
"hideControls": false,
|
||||
"sharedCrosshair": false,
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)",
|
||||
"thresholdLine": false
|
||||
},
|
||||
"id": 1,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
"current": true,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": true,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "",
|
||||
"errors": {},
|
||||
"isCounter": false,
|
||||
"metric": "timely.metrics.received",
|
||||
"refId": "A",
|
||||
"shouldComputeRate": false
|
||||
},
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "",
|
||||
"errors": {},
|
||||
"metric": "timely.keys.meta.inserted",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "",
|
||||
"errors": {},
|
||||
"metric": "timely.keys.metric.inserted",
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Status",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": false,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": "Keys",
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "Row"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 2,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "max",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "max",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1s",
|
||||
"metric": "sys.cpu.user",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CPU User by Host",
|
||||
"tooltip": {
|
||||
"msResolution": false,
|
||||
"shared": true,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 3,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "max",
|
||||
"alias": "$tag_rack",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "max",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1s",
|
||||
"metric": "sys.cpu.user",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"rack": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CPU User by Rack",
|
||||
"tooltip": {
|
||||
"msResolution": false,
|
||||
"shared": true,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 4,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "max",
|
||||
"alias": "",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "max",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1s",
|
||||
"metric": "sys.cpu.user",
|
||||
"refId": "A",
|
||||
"tags": {}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Cumulative CPU",
|
||||
"tooltip": {
|
||||
"msResolution": false,
|
||||
"shared": true,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
}
|
||||
],
|
||||
"time": {
|
||||
"from": "now-15m",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"now": true,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"refresh": "5s",
|
||||
"schemaVersion": 12,
|
||||
"version": 1,
|
||||
"links": []
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
import _ from "lodash";
|
||||
import angular from 'angular';
|
||||
import * as dateMath from '../../../app/core/utils/datemath';
|
||||
|
||||
export class TimelyDatasource {
|
||||
|
||||
// arguments injected by angular
|
||||
constructor(instanceSettings, $q, backendSrv, templateSrv, contextSrv) {
|
||||
this.type = instanceSettings.type;
|
||||
this.name = instanceSettings.name;
|
||||
|
||||
this.jsonData = instanceSettings.jsonData || {};
|
||||
this.httpsPort = instanceSettings.jsonData.httpsPort;
|
||||
this.timelyHost = instanceSettings.jsonData.timelyHost;
|
||||
this.wsPort = instanceSettings.jsonData.wsPort;
|
||||
if( !instanceSettings.jsonData.basicAuths){
|
||||
instanceSettings.jsonData.basicAuths = false;
|
||||
}
|
||||
|
||||
this.url = this._makeHttpsUrl()
|
||||
this.withCredentials = true;
|
||||
this.basicAuth = false;
|
||||
|
||||
this.supportMetrics = true;
|
||||
this.tagKeys = {};
|
||||
|
||||
this.$q = $q;
|
||||
this.backendSrv = backendSrv;
|
||||
this.templateSrv = templateSrv;
|
||||
this.contextSrv = contextSrv;
|
||||
|
||||
this.aggregatorsPromise = null;
|
||||
this.filterTypesPromise = null;
|
||||
|
||||
}
|
||||
|
||||
// REQUIRED INTERFACE
|
||||
query(options){
|
||||
var start = this.convertToTSDBTime(options.rangeRaw.from, false);
|
||||
var end = this.convertToTSDBTime(options.rangeRaw.to, true);
|
||||
var qs = [];
|
||||
|
||||
_.each(options.targets, function(target) {
|
||||
if (!target.metric) { return; }
|
||||
qs.push(this.convertTargetToQuery(target, options));
|
||||
}.bind(this) );
|
||||
|
||||
var queries = _.compact(qs);
|
||||
|
||||
// No valid targets, return the empty result to save a round trip.
|
||||
if (_.isEmpty(queries)) {
|
||||
var d = this.$q.defer();
|
||||
d.resolve({ data: [] });
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
var groupByTags = {};
|
||||
_.each(queries, function(query) {
|
||||
if (query.filters && query.filters.length > 0) {
|
||||
_.each(query.filters, function(val) {
|
||||
groupByTags[val.tagk] = true;
|
||||
});
|
||||
} else {
|
||||
_.each(query.tags, function(val, key) {
|
||||
groupByTags[key] = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return this.performTimeSeriesQuery(queries, start, end).then(function(response) {
|
||||
var metricToTargetMapping = this.mapMetricsToTargets(response.data, options);
|
||||
var result = _.map(response.data, function(metricData, index) {
|
||||
index = metricToTargetMapping[index];
|
||||
if (index === -1) {
|
||||
index = 0;
|
||||
}
|
||||
this._saveTagKeys(metricData);
|
||||
|
||||
return this.transformMetricData(metricData, groupByTags, options.targets[index], options);
|
||||
}.bind(this));
|
||||
return { data: result };
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
// REQUIRED INTERFACE
|
||||
testDatasource(){
|
||||
return this.backendSrv.datasourceRequest({
|
||||
url: this.url + '/version',
|
||||
method: 'GET'
|
||||
}).then(response => {
|
||||
if (response.status === 200) {
|
||||
return { status: "success", message: "Data source is working", title: "Success" };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// REQUIRED INTERFACE
|
||||
annotationQuery(options){
|
||||
var start = convertToTSDBTime(options.rangeRaw.from, false);
|
||||
var end = convertToTSDBTime(options.rangeRaw.to, true);
|
||||
var qs = [];
|
||||
var eventList = [];
|
||||
|
||||
qs.push({ aggregator:"sum", metric:options.annotation.target });
|
||||
|
||||
var queries = _.compact(qs);
|
||||
|
||||
return this.performTimeSeriesQuery(queries, start, end).then(function(results) {
|
||||
if(results.data[0]) {
|
||||
var annotationObject = results.data[0].annotations;
|
||||
if(options.annotation.isGlobal){
|
||||
annotationObject = results.data[0].globalAnnotations;
|
||||
}
|
||||
if(annotationObject) {
|
||||
_.each(annotationObject, function(annotation) {
|
||||
var event = {
|
||||
title: annotation.description,
|
||||
time: Math.floor(annotation.startTime) * 1000,
|
||||
text: annotation.notes,
|
||||
annotation: options.annotation
|
||||
};
|
||||
|
||||
eventList.push(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
return eventList;
|
||||
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
// REQUIRED INTERFACE
|
||||
metricFindQuery(query) {
|
||||
if (!query) { return this.$q.when([]); }
|
||||
|
||||
var interpolated;
|
||||
try {
|
||||
interpolated = this.templateSrv.replace(query);
|
||||
}
|
||||
catch (err) {
|
||||
return this.$q.reject(err);
|
||||
}
|
||||
|
||||
var responseTransform = function(result) {
|
||||
return _.map(result, function(value) {
|
||||
return {text: value};
|
||||
});
|
||||
};
|
||||
|
||||
var metrics_regex = /metrics\((.*)\)/;
|
||||
var tag_names_regex = /tag_names\((.*)\)/;
|
||||
var tag_values_regex = /tag_values\((.*?),\s?(.*)\)/;
|
||||
var tag_names_suggest_regex = /suggest_tagk\((.*)\)/;
|
||||
var tag_values_suggest_regex = /suggest_tagv\((.*)\)/;
|
||||
|
||||
var metrics_query = interpolated.match(metrics_regex);
|
||||
if (metrics_query) {
|
||||
return this._performSuggestQuery(metrics_query[1], 'metrics').then(responseTransform);
|
||||
}
|
||||
|
||||
var tag_names_query = interpolated.match(tag_names_regex);
|
||||
if (tag_names_query) {
|
||||
return this._performMetricKeyLookup(tag_names_query[1]).then(responseTransform);
|
||||
}
|
||||
|
||||
var tag_values_query = interpolated.match(tag_values_regex);
|
||||
if (tag_values_query) {
|
||||
return this._performMetricKeyValueLookup(tag_values_query[1], tag_values_query[2]).then(responseTransform);
|
||||
}
|
||||
|
||||
var tag_names_suggest_query = interpolated.match(tag_names_suggest_regex);
|
||||
if (tag_names_suggest_query) {
|
||||
return this._performSuggestQuery(tag_names_suggest_query[1], 'tagk').then(responseTransform);
|
||||
}
|
||||
|
||||
var tag_values_suggest_query = interpolated.match(tag_values_suggest_regex);
|
||||
if (tag_values_suggest_query) {
|
||||
return this._performSuggestQuery(tag_values_suggest_query[1], 'tagv').then(responseTransform);
|
||||
}
|
||||
|
||||
return this.$q.when([]);
|
||||
}
|
||||
|
||||
performTimeSeriesQuery(queries, start, end){
|
||||
var msResolution = true;
|
||||
|
||||
var reqBody = {
|
||||
start: start,
|
||||
queries: queries,
|
||||
msResolution: msResolution,
|
||||
globalAnnotations: true,
|
||||
showQuery: true
|
||||
};
|
||||
|
||||
// Relative queries (e.g. last hour) don't include an end time
|
||||
if (end) {
|
||||
reqBody.end = end;
|
||||
}
|
||||
|
||||
var options = {
|
||||
method: 'POST',
|
||||
url: this.url + '/api/query',
|
||||
data: reqBody
|
||||
};
|
||||
|
||||
this._addCredentialOptions(options);
|
||||
|
||||
// In case the backend is 3rd-party hosted and does not suport OPTIONS, urlencoded requests
|
||||
// go as POST rather than OPTIONS+POST
|
||||
options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
||||
|
||||
return this.backendSrv.datasourceRequest(options);
|
||||
}
|
||||
|
||||
_saveTagKeys(metricData) {
|
||||
var tagKeys = Object.keys(metricData.tags);
|
||||
_.each(metricData.aggregateTags, function(tag) {
|
||||
tagKeys.push(tag);
|
||||
});
|
||||
|
||||
this.tagKeys[metricData.metric] = tagKeys;
|
||||
}
|
||||
|
||||
suggestTagKeys(metric) {
|
||||
return this.$q.when(this.tagKeys[metric] || []);
|
||||
}
|
||||
|
||||
_performSuggestQuery(query, type) {
|
||||
return this._get('/api/suggest', {type: type, q: query, max: 1000}).then(function(result) {
|
||||
return result.data;
|
||||
});
|
||||
}
|
||||
|
||||
_performMetricKeyValueLookup(metric, keys) {
|
||||
|
||||
if(!metric || !keys) {
|
||||
return $q.when([]);
|
||||
}
|
||||
|
||||
var keysArray = keys.split(",").map(function(key) {
|
||||
return key.trim();
|
||||
});
|
||||
var key = keysArray[0];
|
||||
var keysQuery = key + "=.*";
|
||||
|
||||
if (keysArray.length > 1) {
|
||||
keysQuery += "," + keysArray.splice(1).join(",");
|
||||
}
|
||||
|
||||
var m = metric + "{" + keysQuery + "}";
|
||||
|
||||
return this._get('/api/search/lookup', {m: m, limit: 3000}).then(function(result) {
|
||||
result = result.data.results;
|
||||
var tagvs = [];
|
||||
_.each(result, function(r) {
|
||||
if (tagvs.indexOf(r.tags[key]) === -1) {
|
||||
tagvs.push(r.tags[key]);
|
||||
}
|
||||
});
|
||||
return tagvs;
|
||||
});
|
||||
}
|
||||
|
||||
_performMetricKeyLookup(metric) {
|
||||
if(!metric) { return this.$q.when([]); }
|
||||
|
||||
return this._get('/api/search/lookup', {m: metric, limit: 1000}).then(function(result) {
|
||||
result = result.data.results;
|
||||
var tagks = [];
|
||||
_.each(result, function(r) {
|
||||
_.each(r.tags, function(tagv, tagk) {
|
||||
if(tagks.indexOf(tagk) === -1) {
|
||||
tagks.push(tagk);
|
||||
}
|
||||
});
|
||||
});
|
||||
return tagks;
|
||||
});
|
||||
}
|
||||
|
||||
_get(relativeUrl, params) {
|
||||
var options = {
|
||||
method: 'GET',
|
||||
url: this.url + relativeUrl,
|
||||
params: params,
|
||||
};
|
||||
|
||||
this._addCredentialOptions(options);
|
||||
|
||||
return this.backendSrv.datasourceRequest(options);
|
||||
}
|
||||
|
||||
_addCredentialOptions(options) {
|
||||
if (this.basicAuth || this.withCredentials) {
|
||||
options.withCredentials = true;
|
||||
}
|
||||
if (this.basicAuth) {
|
||||
options.headers = {"Authorization": this.basicAuth};
|
||||
}
|
||||
}
|
||||
|
||||
getAggregators(){
|
||||
if (this.aggregatorsPromise) { return this.aggregatorsPromise; }
|
||||
|
||||
this.aggregatorsPromise = this._get('/api/aggregators').then(function(result) {
|
||||
if (result.data && _.isArray(result.data)) {
|
||||
return result.data.sort();
|
||||
}
|
||||
return [];
|
||||
});
|
||||
return this.aggregatorsPromise;
|
||||
}
|
||||
|
||||
getFilterTypes() {
|
||||
if (this.filterTypesPromise) { return this.filterTypesPromise; }
|
||||
|
||||
// filters are for a newer opentsdb api version than Timely originally copied.
|
||||
this.filterTypesPromise = this._get('/api/aggregators').then(function(result) {
|
||||
return [];
|
||||
});
|
||||
return this.filterTypesPromise;
|
||||
}
|
||||
|
||||
transformMetricData(md, groupByTags, target, options) {
|
||||
var metricLabel = this.createMetricLabel(md, target, groupByTags, options);
|
||||
var dps = [];
|
||||
|
||||
// TSDB returns datapoints has a hash of ts => value.
|
||||
// Can't use _.pairs(invert()) because it stringifies keys/values
|
||||
_.each(md.dps, function (v, k) {
|
||||
dps.push([v, k * 1]);
|
||||
});
|
||||
|
||||
return { target: metricLabel, datapoints: dps };
|
||||
}
|
||||
|
||||
createMetricLabel(md, target, groupByTags, options) {
|
||||
if (target.alias) {
|
||||
var scopedVars = _.clone(options.scopedVars || {});
|
||||
_.each(md.tags, function(value, key) {
|
||||
scopedVars['tag_' + key] = {value: value};
|
||||
});
|
||||
return this.templateSrv.replace(target.alias, scopedVars);
|
||||
}
|
||||
|
||||
var label = md.metric;
|
||||
var tagData = [];
|
||||
|
||||
if (!_.isEmpty(md.tags)) {
|
||||
_.each(_.pairs(md.tags), function(tag) {
|
||||
if (_.has(groupByTags, tag[0])) {
|
||||
tagData.push(tag[0] + "=" + tag[1]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!_.isEmpty(tagData)) {
|
||||
label += "{" + tagData.join(", ") + "}";
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
convertTargetToQuery(target, options) {
|
||||
if (!target.metric || target.hide) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var query = {
|
||||
metric: this.templateSrv.replace(target.metric, options.scopedVars),
|
||||
aggregator: "avg"
|
||||
};
|
||||
|
||||
if (target.aggregator) {
|
||||
query.aggregator = this.templateSrv.replace(target.aggregator);
|
||||
}
|
||||
|
||||
if (target.shouldComputeRate) {
|
||||
query.rate = true;
|
||||
query.rateOptions = {
|
||||
counter: !!target.isCounter
|
||||
};
|
||||
|
||||
if (target.counterMax && target.counterMax.length) {
|
||||
query.rateOptions.counterMax = parseInt(target.counterMax);
|
||||
}
|
||||
|
||||
if (target.counterResetValue && target.counterResetValue.length) {
|
||||
query.rateOptions.resetValue = parseInt(target.counterResetValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (!target.disableDownsampling) {
|
||||
var interval = this.templateSrv.replace(target.downsampleInterval || options.interval);
|
||||
|
||||
if (interval.match(/\.[0-9]+s/)) {
|
||||
interval = parseFloat(interval)*1000 + "ms";
|
||||
}
|
||||
|
||||
query.downsample = interval + "-" + target.downsampleAggregator;
|
||||
|
||||
if (target.downsampleFillPolicy && target.downsampleFillPolicy !== "none") {
|
||||
query.downsample += "-" + target.downsampleFillPolicy;
|
||||
}
|
||||
}
|
||||
|
||||
if (target.filters && target.filters.length > 0) {
|
||||
query.filters = angular.copy(target.filters);
|
||||
if(query.filters){
|
||||
for(var filter_key in query.filters){
|
||||
query.filters[filter_key].filter = this.templateSrv.replace(query.filters[filter_key].filter, options.scopedVars, 'pipe');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
query.tags = angular.copy(target.tags);
|
||||
if(query.tags){
|
||||
for(var tag_key in query.tags){
|
||||
query.tags[tag_key] = this.templateSrv.replace(query.tags[tag_key], options.scopedVars, 'pipe');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
mapMetricsToTargets(metrics, options) {
|
||||
var interpolatedTagValue;
|
||||
return _.map(metrics, function(metricData) {
|
||||
return _.findIndex(options.targets, function(target) {
|
||||
if (target.filters && target.filters.length > 0) {
|
||||
return target.metric === metricData.metric;
|
||||
} else {
|
||||
return target.metric === metricData.metric &&
|
||||
_.all(target.tags,
|
||||
function(tagV, tagK) {
|
||||
interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe');
|
||||
return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === "*";
|
||||
}.bind(this));
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
convertToTSDBTime(date, roundUp) {
|
||||
if (date === 'now') {
|
||||
return null;
|
||||
}
|
||||
|
||||
date = dateMath.parse(date, roundUp);
|
||||
return date.valueOf();
|
||||
}
|
||||
|
||||
_makeHttpsUrl(){
|
||||
return 'https://' + this.timelyHost + ':' + this.httpsPort
|
||||
}
|
||||
|
||||
_makeWsUrl(){
|
||||
return 'ws://' + this.timelyHost + ':' + this.wsPort
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import {TimelyDatasource} from './datasource';
|
||||
import {TimelyQueryCtrl} from './query_ctrl';
|
||||
import {DatasourceConfigCtrl} from './config';
|
||||
|
||||
class TimelyAnnotationsQueryCtrl {}
|
||||
TimelyAnnotationsQueryCtrl.templateUrl = 'components/annotations.editor.html';
|
||||
|
||||
export {
|
||||
TimelyDatasource as Datasource,
|
||||
TimelyQueryCtrl as QueryCtrl,
|
||||
DatasourceConfigCtrl as ConfigCtrl,
|
||||
TimelyAnnotationsQueryCtrl as AnnotationsQueryCtrl
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
<div class="gf-form-group">
|
||||
|
||||
<div class="gf-form-group">
|
||||
<h3 class="page-heading">Server Settings</h3>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-7">Hostname</span>
|
||||
<input class="gf-form-input" type="text"
|
||||
ng-model="ctrl.current.jsonData.timelyHost" placeholder="{{ctrl.current.jsonData.suggestHost}}"
|
||||
min-length="0"
|
||||
ng-pattern="/(\w+:{0,1}\w*@)?(\S+)?$/" required></input>
|
||||
<info-popover mode="right-absolute">
|
||||
<p>Hostname of timely server</p>
|
||||
</info-popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-7">HTTPS Port</span>
|
||||
<input class="gf-form-input" type="text"
|
||||
ng-model="ctrl.current.jsonData.httpsPort"
|
||||
min-length="0"
|
||||
ng-pattern="/[0-9]+/" required></input>
|
||||
<info-popover mode="right-absolute">
|
||||
<p>Port number for Timely HTTPS access</p>
|
||||
</info-popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-7">WS Port</span>
|
||||
<input class="gf-form-input" type="text"
|
||||
ng-model="ctrl.current.jsonData.wsPort"
|
||||
min-length="0"
|
||||
ng-pattern="/[0-9]+/"></input>
|
||||
<info-popover mode="right-absolute">
|
||||
<p>Port number for Timely WebSocket access</p>
|
||||
</info-popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<gf-form-switch class="gf-form "
|
||||
label="Basic Auths"
|
||||
checked="ctrl.current.jsonData.basicAuths" switch-class="max-width-6">
|
||||
</gf-form-switch>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">
|
||||
<a ng-href="https://{{ctrl.current.jsonData.timelyHost}}:{{ctrl.current.jsonData.httpsPort}}/version" target="_blank">
|
||||
Browser Cert Check
|
||||
</a>
|
||||
|
||||
<info-popover mode="right">
|
||||
<p>Opens a new browser to test https access, allows you to accept a cert, etc.</p>
|
||||
</info-popover>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"type": "datasource",
|
||||
"name": "Timely",
|
||||
"id": "timely-datasource",
|
||||
|
||||
"metrics": true,
|
||||
"defaultMatchFormat": "pipe",
|
||||
"annotations": true,
|
||||
|
||||
"includes": [
|
||||
{"type": "dashboard", "name": "Timely Standalone Test", "path": "dashboards/standalone_test.json"},
|
||||
{"type": "dashboard", "name": "Accumulo Overview", "path": "dashboards/AccumuloOverview.json"},
|
||||
{"type": "dashboard", "name": "Hadoop Overview", "path": "dashboards/HadoopOverview.json"},
|
||||
{"type": "dashboard", "name": "System Overview", "path": "dashboards/SystemOverview.json"},
|
||||
{"type": "dashboard", "name": "Timely Status", "path": "dashboards/TimelyStatus.json"}
|
||||
],
|
||||
|
||||
"info": {
|
||||
"author": {
|
||||
"name": "National Security Agency",
|
||||
"url": "http://github.com/NationalSecurityAgency/timely"
|
||||
},
|
||||
"logos": {
|
||||
"small": "../timely-app/img/brand_small.png",
|
||||
"large": "../timely-app/img/brand_large.png"
|
||||
}
|
||||
}
|
||||
}
|
||||