Commit ece8a925 by Torkel Ödegaard

Merge branch 'master' of github.com:grafana/grafana

Conflicts:
	examples/nginx-app/package.json
	examples/nginx-app/plugin.json
parents fc54c01f 3fd420c9
......@@ -64,9 +64,19 @@ Name | Description
`metrics(namespace)` | Returns a list of metrics in the namespace.
`dimension_keys(namespace)` | Returns a list of dimension keys in the namespace.
`dimension_values(region, namespace, metric, dimension_key)` | Returns a list of dimension values matching the specified `region`, `namespace`, `metric` and `dimension_key`.
`ebs_volume_ids(region, instance_id)` | Returns a list of volume id matching the specified `region`, `instance_id`.
`ec2_instance_attribute(region, attribute_name, filters)` | Returns a list of attribute matching the specified `region`, `attribute_name`, `filters`.
For details about the metrics CloudWatch provides, please refer to the [CloudWatch documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html).
The `ec2_instance_attribute` query take `filters` in JSON format.
You can specify [pre-defined filters of ec2:DescribeInstances](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html).
Specify like `{ filter_name1: [ filter_value1 ], filter_name2: [ filter_value2 ] }`
Example `ec2_instance_attribute()` query
ec2_instance_attribute(us-east-1, InstanceId, { "tag:Environment": [ "production" ] })
![](/img/v2/cloudwatch_templating.png)
## Cost
......
......@@ -74,7 +74,7 @@ page_keywords: grafana, admin, http, api, documentation, datasource
"jsonData":null
}
## Get a single data sources by Name
## Get a single data source by Name
`GET /api/datasources/name/:name`
......@@ -107,6 +107,26 @@ page_keywords: grafana, admin, http, api, documentation, datasource
"jsonData":null
}
## Get data source Id by Name
`GET /api/datasources/id/:name`
**Example Request**:
GET /api/datasources/id/test_datasource HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
**Example Response**:
HTTP/1.1 200
Content-Type: application/json
{
"id":1
}
## Create data source
`POST /api/datasources`
......
......@@ -159,19 +159,19 @@ The database user's password (not applicable for `sqlite3`).
For Postgres, use either `disable`, `require` or `verify-full`.
For MySQL, use either `true`, `false`, or `skip-verify`.
### ca_cert_path
### ca_cert_path
(MySQL only) The path to the CA certificate to use. On many linux systems, certs can be found in `/etc/ssl/certs`.
### client_key_path
### client_key_path
(MySQL only) The path to the client key. Only if server requires client authentication.
### client_cert_path
### client_cert_path
(MySQL only) The path to the client cert. Only if server requires client authentication.
### server_cert_name
### server_cert_name
(MySQL only) The common name field of the certificate used by the `mysql` server. Not necessary if `ssl_mode` is set to `skip-verify`.
......@@ -373,7 +373,7 @@ Set to `true` to enable auto sign up of users who do not exist in Grafana DB. De
### provider
Valid values are `memory`, `file`, `mysql`, `postgres`, `memcache`. Default is `file`.
Valid values are `memory`, `file`, `mysql`, `postgres`, `memcache` or `redis`. Default is `file`.
### provider_config
......@@ -384,6 +384,7 @@ session provider you have configured.
- **mysql:** go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1:3306)/database_name`
- **postgres:** ex: user=a password=b host=localhost port=5432 dbname=c sslmode=disable
- **memcache:** ex: 127.0.0.1:11211
- **redis:** ex: `addr=127.0.0.1:6379,pool_size=100,db=grafana`
If you use MySQL or Postgres as the session store you need to create the
session table manually.
......@@ -415,10 +416,10 @@ How long sessions lasts in seconds. Defaults to `86400` (24 hours).
### reporting_enabled
When enabled Grafana will send anonymous usage statistics to
When enabled Grafana will send anonymous usage statistics to
`stats.grafana.org`. No IP addresses are being tracked, only simple counters to
track running instances, versions, dashboard & error counts. It is very helpful
to us, so please leave this enabled. Counters are sent every 24 hours. Default
to us, so please leave this enabled. Counters are sent every 24 hours. Default
value is `true`.
### google_analytics_ua_id
......
......@@ -6,7 +6,7 @@ page_keywords: grafana, ldap, configuration, documentation, integration
# LDAP Integration
Grafana 2.1 ships with a strong LDAP integration feature. The LDAP integration in Grafana allows your
Grafana (2.1 and newer) ships with a strong LDAP integration feature. The LDAP integration in Grafana allows your
Grafana users to login with their LDAP credentials. You can also specify mappings between LDAP
group memberships and Grafana Organization user roles.
......
## Example plugin implementations
[datasource-plugin-genericdatsource](https://github.com/grafana/datasource-plugin-genericdatasource/tree/3.0)
\ No newline at end of file
datasource:[simple-json-datasource](https://github.com/grafana/simple-json-datasource)
app: [example-app](https://github.com/grafana/example-app)
\ No newline at end of file
.DS_Store
node_modules
tmp/*
npm-debug.log
dist/*
{
"disallowImplicitTypeConversion": ["string"],
"disallowKeywords": ["with"],
"disallowMultipleLineBreaks": true,
"disallowMixedSpacesAndTabs": true,
"disallowTrailingWhitespace": true,
"requireSpacesInFunctionExpression": {
"beforeOpeningCurlyBrace": true
},
"disallowSpacesInsideArrayBrackets": true,
"disallowSpacesInsideParentheses": true,
"validateIndentation": 2
}
\ No newline at end of file
{
"browser": true,
"esnext": true,
"bitwise":false,
"curly": true,
"eqnull": true,
"devel": true,
"eqeqeq": true,
"forin": false,
"immed": true,
"supernew": true,
"expr": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"noempty": true,
"undef": true,
"boss": true,
"trailing": true,
"laxbreak": true,
"laxcomma": true,
"sub": true,
"unused": true,
"maxdepth": 6,
"maxlen": 140,
"globals": {
"System": true,
"define": true,
"require": true,
"Chromath": false,
"setImmediate": true
}
}
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'
},
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', 'babel']);
};
{
"name": "kentik-app",
"private": true,
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/raintank/kentik-app-poc.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/raintank/kentik-app-poc/issues"
},
"devDependencies": {
"grunt": "~0.4.5",
"babel": "~6.5.1",
"grunt-babel": "~6.0.0",
"grunt-contrib-copy": "~0.8.2",
"grunt-contrib-watch": "^0.6.1",
"grunt-contrib-uglify": "~0.11.0",
"grunt-systemjs-builder": "^0.2.5",
"load-grunt-tasks": "~3.2.0",
"grunt-execute": "~0.2.2",
"grunt-contrib-clean": "~0.6.0"
},
"dependencies": {
"babel-plugin-transform-es2015-modules-systemjs": "^6.5.0",
"babel-preset-es2015": "^6.5.0",
"lodash": "~4.0.0"
},
"homepage": "https://github.com/raintank/kentik-app-poc#readme"
}
## Overview
This application is an example app.
### Awesome
Even though it does not have any features it is still pretty awesome.
export class NginxAppConfigCtrl {
}
NginxAppConfigCtrl.templateUrl = 'components/config.html';
export class LogsPageCtrl {
}
LogsPageCtrl.templateUrl = 'components/logs.html';
export class StreamPageCtrl {
}
StreamPageCtrl.templateUrl = 'components/stream.html';
export default class NginxDatasource {
constructor() {}
query(options) {
return [];
}
testDatasource() {
return false;
}
}
import {Datasource} from './datasource';
export {
Datasource
};
\ No newline at end of file
{
"type": "datasource",
"name": "Nginx Datasource",
"id": "nginx-datasource"
}
import {LogsPageCtrl} from './components/logs';
import {StreamPageCtrl} from './components/stream';
import {NginxAppConfigCtrl} from './components/config';
export {
NginxAppConfigCtrl as ConfigCtrl,
StreamPageCtrl,
LogsPageCtrl
};
import {PanelCtrl} from 'app/plugins/sdk';
class NginxPanelCtrl extends PanelCtrl {
constructor($scope, $injector) {
super($scope, $injector);
}
}
NginxPanelCtrl.template = '<h2>nginx!</h2>';
export {
NginxPanelCtrl as PanelCtrl
};
{
"type": "panel",
"name": "Nginx Panel",
"id": "nginx-panel"
}
......@@ -167,11 +167,10 @@ func Register(r *macaron.Macaron) {
r.Put("/:id", bind(m.UpdateDataSourceCommand{}), UpdateDataSource)
r.Delete("/:id", DeleteDataSource)
r.Get("/:id", wrap(GetDataSourceById))
r.Get("/name/:name", wrap(GetDataSourceByName))
}, reqOrgAdmin)
r.Group("/datasources/name/:name", func() {
r.Get("/", wrap(GetDataSourceByName))
}, reqOrgAdmin)
r.Get("/datasources/id/:name", wrap(GetDataSourceIdByName), reqSignedIn)
r.Group("/plugins", func() {
r.Get("/", wrap(GetPluginList))
......
......@@ -55,8 +55,10 @@ func init() {
"S3BytesWritten", "S3BytesRead", "HDFSUtilization", "HDFSBytesRead", "HDFSBytesWritten", "MissingBlocks", "CorruptBlocks", "TotalLoad", "MemoryTotalMB", "MemoryReservedMB", "MemoryAvailableMB", "MemoryAllocatedMB", "PendingDeletionBlocks", "UnderReplicatedBlocks", "DfsPendingReplicationBlocks", "CapacityRemainingGB",
"HbaseBackupFailed", "MostRecentBackupDuration", "TimeSinceLastSuccessfulBackup"},
"AWS/ES": {"ClusterStatus.green", "ClusterStatus.yellow", "ClusterStatus.red", "Nodes", "SearchableDocuments", "DeletedDocuments", "CPUUtilization", "FreeStorageSpace", "JVMMemoryPressure", "AutomatedSnapshotFailure", "MasterCPUUtilization", "MasterFreeStorageSpace", "MasterJVMMemoryPressure", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "DiskQueueLength", "ReadIOPS", "WriteIOPS"},
"AWS/Events": {"Invocations", "FailedInvocations", "TriggeredRules", "MatchedEvents", "ThrottledRules"},
"AWS/Kinesis": {"PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "IncomingBytes", "IncomingRecords", "GetRecords.Bytes", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Success"},
"AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles"},
"AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"},
"AWS/ML": {"PredictCount", "PredictFailureCount"},
"AWS/OpsWorks": {"cpu_idle", "cpu_nice", "cpu_system", "cpu_user", "cpu_waitio", "load_1", "load_5", "load_15", "memory_buffers", "memory_cached", "memory_free", "memory_swap", "memory_total", "memory_used", "procs"},
"AWS/Redshift": {"CPUUtilization", "DatabaseConnections", "HealthStatus", "MaintenanceMode", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "PercentageDiskSpaceUsed", "ReadIOPS", "ReadLatency", "ReadThroughput", "WriteIOPS", "WriteLatency", "WriteThroughput"},
......@@ -85,8 +87,10 @@ func init() {
"AWS/ELB": {"LoadBalancerName", "AvailabilityZone"},
"AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"},
"AWS/ES": {},
"AWS/Events": {"RuleName"},
"AWS/Kinesis": {"StreamName"},
"AWS/Lambda": {"FunctionName"},
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
"AWS/ML": {"MLModelId", "RequestMode"},
"AWS/OpsWorks": {"StackId", "LayerId", "InstanceId"},
"AWS/Redshift": {"NodeID", "ClusterIdentifier"},
......
......@@ -116,6 +116,25 @@ func GetDataSourceByName(c *middleware.Context) Response {
return Json(200, &dtos)
}
// Get /api/datasources/id/:name
func GetDataSourceIdByName(c *middleware.Context) Response {
query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}
if err := bus.Dispatch(&query); err != nil {
if err == m.ErrDataSourceNotFound {
return ApiError(404, "Data source not found", nil)
}
return ApiError(500, "Failed to query datasources", err)
}
ds := query.Result
dtos := dtos.AnyId{
Id: ds.Id,
}
return Json(200, &dtos)
}
func convertModelToDtos(ds m.DataSource) dtos.DataSource {
return dtos.DataSource{
Id: ds.Id,
......
......@@ -10,6 +10,10 @@ import (
"github.com/grafana/grafana/pkg/setting"
)
type AnyId struct {
Id int64 `json:"id"`
}
type LoginCommand struct {
User string `json:"user" binding:"Required"`
Password string `json:"password" binding:"Required"`
......
......@@ -48,18 +48,23 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
data.User.LightTheme = true
}
dashboardChildNavs := []*dtos.NavLink{
{Text: "Home", Url: setting.AppSubUrl + "/"},
{Text: "Playlists", Url: setting.AppSubUrl + "/playlists"},
{Text: "Snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots"},
}
if c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR {
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{Divider: true})
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{Text: "New", Url: setting.AppSubUrl + "/dashboard/new"})
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{Text: "Import", Url: setting.AppSubUrl + "/import/dashboard"})
}
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
Text: "Dashboards",
Icon: "icon-gf icon-gf-dashboard",
Url: setting.AppSubUrl + "/",
Children: []*dtos.NavLink{
{Text: "Home", Url: setting.AppSubUrl + "/"},
{Text: "Playlists", Url: setting.AppSubUrl + "/playlists"},
{Text: "Snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots"},
{Divider: true},
{Text: "New", Url: setting.AppSubUrl + "/dashboard/new"},
{Text: "Import", Url: setting.AppSubUrl + "/import/dashboard"},
},
Text: "Dashboards",
Icon: "icon-gf icon-gf-dashboard",
Url: setting.AppSubUrl + "/",
Children: dashboardChildNavs,
})
if c.OrgRole == m.ROLE_ADMIN {
......
......@@ -31,6 +31,7 @@ func RenderToPng(c *middleware.Context) {
Width: queryReader.Get("width", "800"),
Height: queryReader.Get("height", "400"),
SessionId: c.Session.ID(),
Timeout: queryReader.Get("timeout", "15"),
}
renderOpts.Url = setting.ToAbsUrl(renderOpts.Url)
......
......@@ -4,6 +4,7 @@ import (
"archive/zip"
"bytes"
"errors"
"fmt"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
s "github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
......@@ -64,32 +65,33 @@ func InstallPlugin(pluginName, version string, c CommandLine) error {
return err
}
url := v.Url
commit := v.Commit
if version == "" {
version = v.Version
}
downloadURL := url + "/archive/" + commit + ".zip"
downloadURL := fmt.Sprintf("%s/%s/versions/%s/download",
c.GlobalString("repo"),
pluginName,
version)
log.Infof("installing %v @ %v\n", plugin.Id, version)
log.Infof("from url: %v\n", downloadURL)
log.Infof("on commit: %v\n", commit)
log.Infof("into: %v\n", pluginFolder)
err = downloadFile(plugin.Id, pluginFolder, downloadURL)
if err == nil {
log.Infof("Installed %v successfully ✔\n", plugin.Id)
if err != nil {
return err
}
res, _ := s.ReadPlugin(pluginFolder, pluginName)
log.Infof("Installed %v successfully ✔\n", plugin.Id)
/* Enable once we need support for downloading depedencies
res, _ := s.ReadPlugin(pluginFolder, pluginName)
for _, v := range res.Dependency.Plugins {
InstallPlugin(v.Id, version, c)
log.Infof("Installed Dependency: %v ✔\n", v.Id)
log.Infof("Installed dependency: %v ✔\n", v.Id)
}
*/
return err
}
......
......@@ -10,7 +10,7 @@ import (
var ls_getPlugins func(path string) []m.InstalledPlugin = s.GetLocalPlugins
var validateLsCommmand = func(pluginDir string) error {
var validateLsCommand = func(pluginDir string) error {
if pluginDir == "" {
return errors.New("missing path flag")
}
......@@ -31,7 +31,7 @@ var validateLsCommmand = func(pluginDir string) error {
func lsCommand(c CommandLine) error {
pluginDir := c.GlobalString("path")
if err := validateLsCommmand(pluginDir); err != nil {
if err := validateLsCommand(pluginDir); err != nil {
return err
}
......
......@@ -9,10 +9,10 @@ import (
)
func TestMissingPath(t *testing.T) {
var org = validateLsCommmand
var org = validateLsCommand
Convey("ls command", t, func() {
validateLsCommmand = org
validateLsCommand = org
Convey("Missing path", func() {
commandLine := &commandstest.FakeCommandLine{
......@@ -61,7 +61,7 @@ func TestMissingPath(t *testing.T) {
},
}
validateLsCommmand = func(pluginDir string) error {
validateLsCommand = func(pluginDir string) error {
return errors.New("dummie error")
}
......
......@@ -41,7 +41,7 @@ func main() {
cli.StringFlag{
Name: "repo",
Usage: "url to the plugin repository",
Value: "https://raw.githubusercontent.com/grafana/grafana-plugin-repository/master/repo.json",
Value: "",
},
cli.BoolFlag{
Name: "debug, d",
......
......@@ -12,7 +12,8 @@ import (
var IoHelper m.IoUtil = IoUtilImp{}
func ListAllPlugins(repoUrl string) (m.PluginRepo, error) {
res, _ := goreq.Request{Uri: repoUrl}.Do()
res, _ := goreq.Request{Uri: repoUrl + "/repo", MaxRedirects: 3}.Do()
var resp m.PluginRepo
err := res.Body.FromJsonTo(&resp)
......
......@@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"strconv"
)
type RenderOpts struct {
......@@ -18,6 +19,7 @@ type RenderOpts struct {
Width string
Height string
SessionId string
Timeout string
}
func RenderToPng(params *RenderOpts) (string, error) {
......@@ -60,8 +62,13 @@ func RenderToPng(params *RenderOpts) (string, error) {
close(done)
}()
timeout, err := strconv.Atoi(params.Timeout)
if err != nil {
timeout = 15
}
select {
case <-time.After(15 * time.Second):
case <-time.After(time.Duration(timeout) * time.Second):
if err := cmd.Process.Kill(); err != nil {
log.Error(4, "failed to kill: %v", err)
}
......
......@@ -14,8 +14,8 @@ func TestPluginDashboards(t *testing.T) {
Convey("When asking plugin dashboard info", t, func() {
setting.Cfg = ini.Empty()
sec, _ := setting.Cfg.NewSection("plugin.nginx-app")
sec.NewKey("path", "../../examples/nginx-app")
sec, _ := setting.Cfg.NewSection("plugin.test-app")
sec.NewKey("path", "../../tests/test-app")
err := Init()
So(err, ShouldBeNil)
......@@ -31,7 +31,7 @@ func TestPluginDashboards(t *testing.T) {
return m.ErrDashboardNotFound
})
dashboards, err := GetPluginDashboards(1, "nginx-app")
dashboards, err := GetPluginDashboards(1, "test-app")
So(err, ShouldBeNil)
......@@ -43,7 +43,7 @@ func TestPluginDashboards(t *testing.T) {
So(dashboards[0].Title, ShouldEqual, "Nginx Connections")
So(dashboards[0].Revision, ShouldEqual, "1.5")
So(dashboards[0].InstalledRevision, ShouldEqual, "1.1")
So(dashboards[0].InstalledURI, ShouldEqual, "db/nginx-connections")
So(dashboards[0].InstalledUri, ShouldEqual, "db/nginx-connections")
So(dashboards[1].Revision, ShouldEqual, "2.0")
So(dashboards[1].InstalledRevision, ShouldEqual, "")
......
......@@ -28,14 +28,14 @@ func TestPluginScans(t *testing.T) {
Convey("When reading app plugin definition", t, func() {
setting.Cfg = ini.Empty()
sec, _ := setting.Cfg.NewSection("plugin.nginx-app")
sec.NewKey("path", "../../examples/nginx-app")
sec.NewKey("path", "../../tests/test-app")
err := Init()
So(err, ShouldBeNil)
So(len(Apps), ShouldBeGreaterThan, 0)
So(Apps["nginx-app"].Info.Logos.Large, ShouldEqual, "public/plugins/nginx-app/img/logo_large.png")
So(Apps["nginx-app"].Info.Screenshots[1].Path, ShouldEqual, "public/plugins/nginx-app/img/screenshot2.png")
So(Apps["test-app"].Info.Logos.Large, ShouldEqual, "public/plugins/test-app/img/logo_large.png")
So(Apps["test-app"].Info.Screenshots[1].Path, ShouldEqual, "public/plugins/test-app/img/screenshot2.png")
})
}
......@@ -74,7 +74,7 @@ function (angular, _, kbn) {
if (urlValue !== void 0) {
return self.setVariableFromUrl(variable, urlValue).then(lock.resolve);
}
else if (variable.refresh === 'On Dashboard Load' || variable.refresh === 'On Time Change and Dashboard Load') {
else if (variable.refresh === 1 || variable.refresh === 2) {
return self.updateOptions(variable).then(function() {
if (_.isEmpty(variable.current) && variable.options.length) {
console.log("setting current for %s", variable.name);
......
......@@ -143,7 +143,7 @@ function (angular, _, moment, dateMath, CloudWatchAnnotationQuery) {
return this.awsRequest({
region: region,
action: 'DescribeInstances',
parameters: { filter: filters, instanceIds: instanceIds }
parameters: { filters: filters, instanceIds: instanceIds }
});
};
......@@ -205,6 +205,28 @@ function (angular, _, moment, dateMath, CloudWatchAnnotationQuery) {
});
}
var ec2InstanceAttributeQuery = query.match(/^ec2_instance_attribute\(([^,]+?),\s?([^,]+?),\s?(.+?)\)/);
if (ec2InstanceAttributeQuery) {
region = templateSrv.replace(ec2InstanceAttributeQuery[1]);
var filterJson = JSON.parse(templateSrv.replace(ec2InstanceAttributeQuery[3]));
var filters = _.map(filterJson, function(values, name) {
return {
Name: name,
Values: values
};
});
var targetAttributeName = templateSrv.replace(ec2InstanceAttributeQuery[2]);
return this.performEC2DescribeInstances(region, filters, null).then(function(result) {
var attributes = _.chain(result.Reservations)
.map(function(reservations) {
return _.pluck(reservations.Instances, targetAttributeName);
})
.flatten().value();
return transformSuggestData(attributes);
});
}
return $q.when([]);
};
......
......@@ -104,7 +104,7 @@ export function InfluxDatasource(instanceSettings, $q, backendSrv, templateSrv)
this.metricFindQuery = function (query) {
var interpolated;
try {
interpolated = templateSrv.replace(query);
interpolated = templateSrv.replace(query, null, 'regex');
} catch (err) {
return $q.reject(err);
}
......
......@@ -258,6 +258,6 @@
"annotations": {
"enable": false
},
"refresh": "Never",
"refresh": 0,
"version": 6
}
{
"type": "app",
"name": "Nginx",
"id": "nginx-app",
"name": "Test App",
"id": "test-app",
"staticRoot": ".",
......@@ -16,12 +16,12 @@
},
"info": {
"description": "Official Grafana Nginx App & Dashboard bundle",
"description": "Official Grafana Test App & Dashboard bundle",
"author": {
"name": "Nginx Inc.",
"url": "http://nginx.com"
"name": "Test Inc.",
"url": "http://test.com"
},
"keywords": ["nginx"],
"keywords": ["test"],
"logos": {
"small": "img/logo_small.png",
"large": "img/logo_large.png"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment