Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
f1889003
Commit
f1889003
authored
Feb 05, 2016
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of github.com:grafana/grafana
parents
f80d5f8c
fcc960e9
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
486 additions
and
42 deletions
+486
-42
docs/sources/reference/http_api.md
+1
-1
pkg/api/api.go
+8
-1
pkg/api/cloudwatch/metrics.go
+163
-8
pkg/api/cloudwatch/metrics_test.go
+63
-0
pkg/api/dashboard.go
+22
-13
pkg/api/dashboard_snapshot.go
+40
-1
pkg/api/dtos/models.go
+2
-0
pkg/api/index.go
+6
-0
pkg/models/dashboard_snapshot.go
+26
-0
pkg/models/dashboards.go
+7
-3
pkg/services/sqlstore/dashboard_snapshot.go
+16
-0
pkg/services/sqlstore/migrations/dashboard_mig.go
+5
-0
public/app/core/routes/all.js
+5
-0
public/app/features/all.js
+1
-0
public/app/features/dashboard/partials/settings.html
+29
-7
public/app/features/snapshot/all.ts
+1
-0
public/app/features/snapshot/partials/snapshots.html
+39
-0
public/app/features/snapshot/snapshot_ctrl.ts
+42
-0
public/app/plugins/datasource/cloudwatch/datasource.js
+8
-6
public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.js
+2
-2
No files found.
docs/sources/reference/http_api.md
View file @
f1889003
...
@@ -75,7 +75,7 @@ Creates a new dashboard or updates an existing dashboard.
...
@@ -75,7 +75,7 @@ Creates a new dashboard or updates an existing dashboard.
JSON Body schema:
JSON Body schema:
-
**dashboard**
– The complete dashboard model, id = null to create a new dashboard
-
**dashboard**
– The complete dashboard model, id = null to create a new dashboard
.
-
**overwrite**
– Set to true if you want to overwrite existing dashboard with newer version or with same dashboard title.
-
**overwrite**
– Set to true if you want to overwrite existing dashboard with newer version or with same dashboard title.
**Example Response**
:
**Example Response**
:
...
...
pkg/api/api.go
View file @
f1889003
...
@@ -69,9 +69,11 @@ func Register(r *macaron.Macaron) {
...
@@ -69,9 +69,11 @@ func Register(r *macaron.Macaron) {
r
.
Post
(
"/api/user/password/reset"
,
bind
(
dtos
.
ResetUserPasswordForm
{}),
wrap
(
ResetPassword
))
r
.
Post
(
"/api/user/password/reset"
,
bind
(
dtos
.
ResetUserPasswordForm
{}),
wrap
(
ResetPassword
))
// dashboard snapshots
// dashboard snapshots
r
.
Post
(
"/api/snapshots/"
,
bind
(
m
.
CreateDashboardSnapshotCommand
{}),
CreateDashboardSnapshot
)
r
.
Get
(
"/dashboard/snapshot/*"
,
Index
)
r
.
Get
(
"/dashboard/snapshot/*"
,
Index
)
r
.
Get
(
"/dashboard/snapshots/"
,
reqSignedIn
,
Index
)
// api for dashboard snapshots
r
.
Post
(
"/api/snapshots/"
,
bind
(
m
.
CreateDashboardSnapshotCommand
{}),
CreateDashboardSnapshot
)
r
.
Get
(
"/api/snapshot/shared-options/"
,
GetSharingOptions
)
r
.
Get
(
"/api/snapshot/shared-options/"
,
GetSharingOptions
)
r
.
Get
(
"/api/snapshots/:key"
,
GetDashboardSnapshot
)
r
.
Get
(
"/api/snapshots/:key"
,
GetDashboardSnapshot
)
r
.
Get
(
"/api/snapshots-delete/:key"
,
DeleteDashboardSnapshot
)
r
.
Get
(
"/api/snapshots-delete/:key"
,
DeleteDashboardSnapshot
)
...
@@ -183,6 +185,11 @@ func Register(r *macaron.Macaron) {
...
@@ -183,6 +185,11 @@ func Register(r *macaron.Macaron) {
r
.
Get
(
"/tags"
,
GetDashboardTags
)
r
.
Get
(
"/tags"
,
GetDashboardTags
)
})
})
// Dashboard snapshots
r
.
Group
(
"/dashboard/snapshots"
,
func
()
{
r
.
Get
(
"/"
,
wrap
(
SearchDashboardSnapshots
))
})
// Playlist
// Playlist
r
.
Group
(
"/playlists"
,
func
()
{
r
.
Group
(
"/playlists"
,
func
()
{
r
.
Get
(
"/"
,
wrap
(
SearchPlaylists
))
r
.
Get
(
"/"
,
wrap
(
SearchPlaylists
))
...
...
pkg/api/cloudwatch/metrics.go
View file @
f1889003
...
@@ -3,7 +3,14 @@ package cloudwatch
...
@@ -3,7 +3,14 @@ package cloudwatch
import
(
import
(
"encoding/json"
"encoding/json"
"sort"
"sort"
"strings"
"sync"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util"
)
)
...
@@ -11,6 +18,14 @@ import (
...
@@ -11,6 +18,14 @@ import (
var
metricsMap
map
[
string
][]
string
var
metricsMap
map
[
string
][]
string
var
dimensionsMap
map
[
string
][]
string
var
dimensionsMap
map
[
string
][]
string
type
CustomMetricsCache
struct
{
Expire
time
.
Time
Cache
[]
string
}
var
customMetricsMetricsMap
map
[
string
]
map
[
string
]
map
[
string
]
*
CustomMetricsCache
var
customMetricsDimensionsMap
map
[
string
]
map
[
string
]
map
[
string
]
*
CustomMetricsCache
func
init
()
{
func
init
()
{
metricsMap
=
map
[
string
][]
string
{
metricsMap
=
map
[
string
][]
string
{
"AWS/AutoScaling"
:
{
"GroupMinSize"
,
"GroupMaxSize"
,
"GroupDesiredCapacity"
,
"GroupInServiceInstances"
,
"GroupPendingInstances"
,
"GroupStandbyInstances"
,
"GroupTerminatingInstances"
,
"GroupTotalInstances"
},
"AWS/AutoScaling"
:
{
"GroupMinSize"
,
"GroupMaxSize"
,
"GroupDesiredCapacity"
,
"GroupInServiceInstances"
,
"GroupPendingInstances"
,
"GroupStandbyInstances"
,
"GroupTerminatingInstances"
,
"GroupTotalInstances"
},
...
@@ -85,6 +100,9 @@ func init() {
...
@@ -85,6 +100,9 @@ func init() {
"AWS/WAF"
:
{
"Rule"
,
"WebACL"
},
"AWS/WAF"
:
{
"Rule"
,
"WebACL"
},
"AWS/WorkSpaces"
:
{
"DirectoryId"
,
"WorkspaceId"
},
"AWS/WorkSpaces"
:
{
"DirectoryId"
,
"WorkspaceId"
},
}
}
customMetricsMetricsMap
=
make
(
map
[
string
]
map
[
string
]
map
[
string
]
*
CustomMetricsCache
)
customMetricsDimensionsMap
=
make
(
map
[
string
]
map
[
string
]
map
[
string
]
*
CustomMetricsCache
)
}
}
// Whenever this list is updated, frontend list should also be updated.
// Whenever this list is updated, frontend list should also be updated.
...
@@ -127,10 +145,19 @@ func handleGetMetrics(req *cwRequest, c *middleware.Context) {
...
@@ -127,10 +145,19 @@ func handleGetMetrics(req *cwRequest, c *middleware.Context) {
json
.
Unmarshal
(
req
.
Body
,
reqParam
)
json
.
Unmarshal
(
req
.
Body
,
reqParam
)
namespaceMetrics
,
exists
:=
metricsMap
[
reqParam
.
Parameters
.
Namespace
]
var
namespaceMetrics
[]
string
if
!
exists
{
if
!
isCustomMetrics
(
reqParam
.
Parameters
.
Namespace
)
{
c
.
JsonApiErr
(
404
,
"Unable to find namespace "
+
reqParam
.
Parameters
.
Namespace
,
nil
)
var
exists
bool
return
if
namespaceMetrics
,
exists
=
metricsMap
[
reqParam
.
Parameters
.
Namespace
];
!
exists
{
c
.
JsonApiErr
(
404
,
"Unable to find namespace "
+
reqParam
.
Parameters
.
Namespace
,
nil
)
return
}
}
else
{
var
err
error
if
namespaceMetrics
,
err
=
getMetricsForCustomMetrics
(
req
.
Region
,
reqParam
.
Parameters
.
Namespace
,
req
.
DataSource
.
Database
,
getAllMetrics
);
err
!=
nil
{
c
.
JsonApiErr
(
500
,
"Unable to call AWS API"
,
err
)
return
}
}
}
sort
.
Sort
(
sort
.
StringSlice
(
namespaceMetrics
))
sort
.
Sort
(
sort
.
StringSlice
(
namespaceMetrics
))
...
@@ -151,10 +178,19 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) {
...
@@ -151,10 +178,19 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) {
json
.
Unmarshal
(
req
.
Body
,
reqParam
)
json
.
Unmarshal
(
req
.
Body
,
reqParam
)
dimensionValues
,
exists
:=
dimensionsMap
[
reqParam
.
Parameters
.
Namespace
]
var
dimensionValues
[]
string
if
!
exists
{
if
!
isCustomMetrics
(
reqParam
.
Parameters
.
Namespace
)
{
c
.
JsonApiErr
(
404
,
"Unable to find dimension "
+
reqParam
.
Parameters
.
Namespace
,
nil
)
var
exists
bool
return
if
dimensionValues
,
exists
=
dimensionsMap
[
reqParam
.
Parameters
.
Namespace
];
!
exists
{
c
.
JsonApiErr
(
404
,
"Unable to find dimension "
+
reqParam
.
Parameters
.
Namespace
,
nil
)
return
}
}
else
{
var
err
error
if
dimensionValues
,
err
=
getDimensionsForCustomMetrics
(
req
.
Region
,
reqParam
.
Parameters
.
Namespace
,
req
.
DataSource
.
Database
,
getAllMetrics
);
err
!=
nil
{
c
.
JsonApiErr
(
500
,
"Unable to call AWS API"
,
err
)
return
}
}
}
sort
.
Sort
(
sort
.
StringSlice
(
dimensionValues
))
sort
.
Sort
(
sort
.
StringSlice
(
dimensionValues
))
...
@@ -165,3 +201,122 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) {
...
@@ -165,3 +201,122 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) {
c
.
JSON
(
200
,
result
)
c
.
JSON
(
200
,
result
)
}
}
func
getAllMetrics
(
region
string
,
namespace
string
,
database
string
)
(
cloudwatch
.
ListMetricsOutput
,
error
)
{
cfg
:=
&
aws
.
Config
{
Region
:
aws
.
String
(
region
),
Credentials
:
getCredentials
(
database
),
}
svc
:=
cloudwatch
.
New
(
session
.
New
(
cfg
),
cfg
)
params
:=
&
cloudwatch
.
ListMetricsInput
{
Namespace
:
aws
.
String
(
namespace
),
}
var
resp
cloudwatch
.
ListMetricsOutput
err
:=
svc
.
ListMetricsPages
(
params
,
func
(
page
*
cloudwatch
.
ListMetricsOutput
,
lastPage
bool
)
bool
{
metrics
,
_
:=
awsutil
.
ValuesAtPath
(
page
,
"Metrics"
)
for
_
,
metric
:=
range
metrics
{
resp
.
Metrics
=
append
(
resp
.
Metrics
,
metric
.
(
*
cloudwatch
.
Metric
))
}
return
!
lastPage
})
if
err
!=
nil
{
return
resp
,
err
}
return
resp
,
nil
}
var
metricsCacheLock
sync
.
Mutex
func
getMetricsForCustomMetrics
(
region
string
,
namespace
string
,
database
string
,
getAllMetrics
func
(
string
,
string
,
string
)
(
cloudwatch
.
ListMetricsOutput
,
error
))
([]
string
,
error
)
{
result
,
err
:=
getAllMetrics
(
region
,
namespace
,
database
)
if
err
!=
nil
{
return
[]
string
{},
err
}
metricsCacheLock
.
Lock
()
defer
metricsCacheLock
.
Unlock
()
if
_
,
ok
:=
customMetricsMetricsMap
[
database
];
!
ok
{
customMetricsMetricsMap
[
database
]
=
make
(
map
[
string
]
map
[
string
]
*
CustomMetricsCache
)
}
if
_
,
ok
:=
customMetricsMetricsMap
[
database
][
region
];
!
ok
{
customMetricsMetricsMap
[
database
][
region
]
=
make
(
map
[
string
]
*
CustomMetricsCache
)
}
if
_
,
ok
:=
customMetricsMetricsMap
[
database
][
region
][
namespace
];
!
ok
{
customMetricsMetricsMap
[
database
][
region
][
namespace
]
=
&
CustomMetricsCache
{}
customMetricsMetricsMap
[
database
][
region
][
namespace
]
.
Cache
=
make
([]
string
,
0
)
}
if
customMetricsMetricsMap
[
database
][
region
][
namespace
]
.
Expire
.
After
(
time
.
Now
())
{
return
customMetricsMetricsMap
[
database
][
region
][
namespace
]
.
Cache
,
nil
}
customMetricsMetricsMap
[
database
][
region
][
namespace
]
.
Cache
=
make
([]
string
,
0
)
customMetricsMetricsMap
[
database
][
region
][
namespace
]
.
Expire
=
time
.
Now
()
.
Add
(
5
*
time
.
Minute
)
for
_
,
metric
:=
range
result
.
Metrics
{
if
isDuplicate
(
customMetricsMetricsMap
[
database
][
region
][
namespace
]
.
Cache
,
*
metric
.
MetricName
)
{
continue
}
customMetricsMetricsMap
[
database
][
region
][
namespace
]
.
Cache
=
append
(
customMetricsMetricsMap
[
database
][
region
][
namespace
]
.
Cache
,
*
metric
.
MetricName
)
}
return
customMetricsMetricsMap
[
database
][
region
][
namespace
]
.
Cache
,
nil
}
var
dimensionsCacheLock
sync
.
Mutex
func
getDimensionsForCustomMetrics
(
region
string
,
namespace
string
,
database
string
,
getAllMetrics
func
(
string
,
string
,
string
)
(
cloudwatch
.
ListMetricsOutput
,
error
))
([]
string
,
error
)
{
result
,
err
:=
getAllMetrics
(
region
,
namespace
,
database
)
if
err
!=
nil
{
return
[]
string
{},
err
}
dimensionsCacheLock
.
Lock
()
defer
dimensionsCacheLock
.
Unlock
()
if
_
,
ok
:=
customMetricsDimensionsMap
[
database
];
!
ok
{
customMetricsDimensionsMap
[
database
]
=
make
(
map
[
string
]
map
[
string
]
*
CustomMetricsCache
)
}
if
_
,
ok
:=
customMetricsDimensionsMap
[
database
][
region
];
!
ok
{
customMetricsDimensionsMap
[
database
][
region
]
=
make
(
map
[
string
]
*
CustomMetricsCache
)
}
if
_
,
ok
:=
customMetricsDimensionsMap
[
database
][
region
][
namespace
];
!
ok
{
customMetricsDimensionsMap
[
database
][
region
][
namespace
]
=
&
CustomMetricsCache
{}
customMetricsDimensionsMap
[
database
][
region
][
namespace
]
.
Cache
=
make
([]
string
,
0
)
}
if
customMetricsDimensionsMap
[
database
][
region
][
namespace
]
.
Expire
.
After
(
time
.
Now
())
{
return
customMetricsDimensionsMap
[
database
][
region
][
namespace
]
.
Cache
,
nil
}
customMetricsDimensionsMap
[
database
][
region
][
namespace
]
.
Cache
=
make
([]
string
,
0
)
customMetricsDimensionsMap
[
database
][
region
][
namespace
]
.
Expire
=
time
.
Now
()
.
Add
(
5
*
time
.
Minute
)
for
_
,
metric
:=
range
result
.
Metrics
{
for
_
,
dimension
:=
range
metric
.
Dimensions
{
if
isDuplicate
(
customMetricsDimensionsMap
[
database
][
region
][
namespace
]
.
Cache
,
*
dimension
.
Name
)
{
continue
}
customMetricsDimensionsMap
[
database
][
region
][
namespace
]
.
Cache
=
append
(
customMetricsDimensionsMap
[
database
][
region
][
namespace
]
.
Cache
,
*
dimension
.
Name
)
}
}
return
customMetricsDimensionsMap
[
database
][
region
][
namespace
]
.
Cache
,
nil
}
func
isDuplicate
(
nameList
[]
string
,
target
string
)
bool
{
for
_
,
name
:=
range
nameList
{
if
name
==
target
{
return
true
}
}
return
false
}
func
isCustomMetrics
(
namespace
string
)
bool
{
return
strings
.
Index
(
namespace
,
"AWS/"
)
!=
0
}
pkg/api/cloudwatch/metrics_test.go
0 → 100644
View file @
f1889003
package
cloudwatch
import
(
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
.
"github.com/smartystreets/goconvey/convey"
)
func
TestCloudWatchMetrics
(
t
*
testing
.
T
)
{
Convey
(
"When calling getMetricsForCustomMetrics"
,
t
,
func
()
{
region
:=
"us-east-1"
namespace
:=
"Foo"
database
:=
"default"
f
:=
func
(
region
string
,
namespace
string
,
database
string
)
(
cloudwatch
.
ListMetricsOutput
,
error
)
{
return
cloudwatch
.
ListMetricsOutput
{
Metrics
:
[]
*
cloudwatch
.
Metric
{
{
MetricName
:
aws
.
String
(
"Test_MetricName"
),
Dimensions
:
[]
*
cloudwatch
.
Dimension
{
{
Name
:
aws
.
String
(
"Test_DimensionName"
),
},
},
},
},
},
nil
}
metrics
,
_
:=
getMetricsForCustomMetrics
(
region
,
namespace
,
database
,
f
)
Convey
(
"Should contain Test_MetricName"
,
func
()
{
So
(
metrics
,
ShouldContain
,
"Test_MetricName"
)
})
})
Convey
(
"When calling getDimensionsForCustomMetrics"
,
t
,
func
()
{
region
:=
"us-east-1"
namespace
:=
"Foo"
database
:=
"default"
f
:=
func
(
region
string
,
namespace
string
,
database
string
)
(
cloudwatch
.
ListMetricsOutput
,
error
)
{
return
cloudwatch
.
ListMetricsOutput
{
Metrics
:
[]
*
cloudwatch
.
Metric
{
{
MetricName
:
aws
.
String
(
"Test_MetricName"
),
Dimensions
:
[]
*
cloudwatch
.
Dimension
{
{
Name
:
aws
.
String
(
"Test_DimensionName"
),
},
},
},
},
},
nil
}
dimensionKeys
,
_
:=
getDimensionsForCustomMetrics
(
region
,
namespace
,
database
,
f
)
Convey
(
"Should contain Test_DimensionName"
,
func
()
{
So
(
dimensionKeys
,
ShouldContain
,
"Test_DimensionName"
)
})
})
}
pkg/api/dashboard.go
View file @
f1889003
...
@@ -49,17 +49,13 @@ func GetDashboard(c *middleware.Context) {
...
@@ -49,17 +49,13 @@ func GetDashboard(c *middleware.Context) {
dash
:=
query
.
Result
dash
:=
query
.
Result
// Finding the last updater of the dashboard
// Finding creator and last updater of the dashboard
updater
:=
"Anonymous"
updater
,
creator
:=
"Anonymous"
,
"Anonymous"
if
dash
.
UpdatedBy
!=
0
{
if
dash
.
UpdatedBy
>
0
{
userQuery
:=
m
.
GetUserByIdQuery
{
Id
:
dash
.
UpdatedBy
}
updater
=
getUserLogin
(
dash
.
UpdatedBy
)
userErr
:=
bus
.
Dispatch
(
&
userQuery
)
}
if
userErr
!=
nil
{
if
dash
.
CreatedBy
>
0
{
updater
=
"Unknown"
creator
=
getUserLogin
(
dash
.
CreatedBy
)
}
else
{
user
:=
userQuery
.
Result
updater
=
user
.
Login
}
}
}
dto
:=
dtos
.
DashboardFullWithMeta
{
dto
:=
dtos
.
DashboardFullWithMeta
{
...
@@ -74,12 +70,25 @@ func GetDashboard(c *middleware.Context) {
...
@@ -74,12 +70,25 @@ func GetDashboard(c *middleware.Context) {
Created
:
dash
.
Created
,
Created
:
dash
.
Created
,
Updated
:
dash
.
Updated
,
Updated
:
dash
.
Updated
,
UpdatedBy
:
updater
,
UpdatedBy
:
updater
,
CreatedBy
:
creator
,
Version
:
dash
.
Version
,
},
},
}
}
c
.
JSON
(
200
,
dto
)
c
.
JSON
(
200
,
dto
)
}
}
func
getUserLogin
(
userId
int64
)
string
{
query
:=
m
.
GetUserByIdQuery
{
Id
:
userId
}
err
:=
bus
.
Dispatch
(
&
query
)
if
err
!=
nil
{
return
"Anonymous"
}
else
{
user
:=
query
.
Result
return
user
.
Login
}
}
func
DeleteDashboard
(
c
*
middleware
.
Context
)
{
func
DeleteDashboard
(
c
*
middleware
.
Context
)
{
slug
:=
c
.
Params
(
":slug"
)
slug
:=
c
.
Params
(
":slug"
)
...
@@ -104,9 +113,9 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) {
...
@@ -104,9 +113,9 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) {
cmd
.
OrgId
=
c
.
OrgId
cmd
.
OrgId
=
c
.
OrgId
if
!
c
.
IsSignedIn
{
if
!
c
.
IsSignedIn
{
cmd
.
U
pdatedBy
=
0
cmd
.
U
serId
=
-
1
}
else
{
}
else
{
cmd
.
U
pdatedBy
=
c
.
UserId
cmd
.
U
serId
=
c
.
UserId
}
}
dash
:=
cmd
.
GetDashboardModel
()
dash
:=
cmd
.
GetDashboardModel
()
...
...
pkg/api/dashboard_snapshot.go
View file @
f1889003
...
@@ -36,7 +36,6 @@ func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapsho
...
@@ -36,7 +36,6 @@ func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapsho
cmd
.
DeleteKey
=
util
.
GetRandomString
(
32
)
cmd
.
DeleteKey
=
util
.
GetRandomString
(
32
)
cmd
.
OrgId
=
c
.
OrgId
cmd
.
OrgId
=
c
.
OrgId
cmd
.
UserId
=
c
.
UserId
cmd
.
UserId
=
c
.
UserId
cmd
.
Name
=
c
.
Name
metrics
.
M_Api_Dashboard_Snapshot_Create
.
Inc
(
1
)
metrics
.
M_Api_Dashboard_Snapshot_Create
.
Inc
(
1
)
}
}
...
@@ -99,3 +98,43 @@ func DeleteDashboardSnapshot(c *middleware.Context) {
...
@@ -99,3 +98,43 @@ func DeleteDashboardSnapshot(c *middleware.Context) {
c
.
JSON
(
200
,
util
.
DynMap
{
"message"
:
"Snapshot deleted. It might take an hour before it's cleared from a CDN cache."
})
c
.
JSON
(
200
,
util
.
DynMap
{
"message"
:
"Snapshot deleted. It might take an hour before it's cleared from a CDN cache."
})
}
}
func
SearchDashboardSnapshots
(
c
*
middleware
.
Context
)
Response
{
query
:=
c
.
Query
(
"query"
)
limit
:=
c
.
QueryInt
(
"limit"
)
if
limit
==
0
{
limit
=
1000
}
searchQuery
:=
m
.
GetDashboardSnapshotsQuery
{
Name
:
query
,
Limit
:
limit
,
OrgId
:
c
.
OrgId
,
}
err
:=
bus
.
Dispatch
(
&
searchQuery
)
if
err
!=
nil
{
return
ApiError
(
500
,
"Search failed"
,
err
)
}
dtos
:=
make
([]
*
m
.
DashboardSnapshotDTO
,
len
(
searchQuery
.
Result
))
for
i
,
snapshot
:=
range
searchQuery
.
Result
{
dtos
[
i
]
=
&
m
.
DashboardSnapshotDTO
{
Id
:
snapshot
.
Id
,
Name
:
snapshot
.
Name
,
Key
:
snapshot
.
Key
,
DeleteKey
:
snapshot
.
DeleteKey
,
OrgId
:
snapshot
.
OrgId
,
UserId
:
snapshot
.
UserId
,
External
:
snapshot
.
External
,
ExternalUrl
:
snapshot
.
ExternalUrl
,
Expires
:
snapshot
.
Expires
,
Created
:
snapshot
.
Created
,
Updated
:
snapshot
.
Updated
,
}
}
return
Json
(
200
,
dtos
)
//return Json(200, searchQuery.Result)
}
pkg/api/dtos/models.go
View file @
f1889003
...
@@ -42,6 +42,8 @@ type DashboardMeta struct {
...
@@ -42,6 +42,8 @@ type DashboardMeta struct {
Created
time
.
Time
`json:"created"`
Created
time
.
Time
`json:"created"`
Updated
time
.
Time
`json:"updated"`
Updated
time
.
Time
`json:"updated"`
UpdatedBy
string
`json:"updatedBy"`
UpdatedBy
string
`json:"updatedBy"`
CreatedBy
string
`json:"createdBy"`
Version
int
`json:"version"`
}
}
type
DashboardFullWithMeta
struct
{
type
DashboardFullWithMeta
struct
{
...
...
pkg/api/index.go
View file @
f1889003
...
@@ -60,6 +60,12 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
...
@@ -60,6 +60,12 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
Url
:
"/playlists"
,
Url
:
"/playlists"
,
})
})
data
.
MainNavLinks
=
append
(
data
.
MainNavLinks
,
&
dtos
.
NavLink
{
Text
:
"Snapshots"
,
Icon
:
"fa fa-fw fa-camera-retro"
,
Url
:
"/dashboard/snapshots"
,
})
if
c
.
OrgRole
==
m
.
ROLE_ADMIN
{
if
c
.
OrgRole
==
m
.
ROLE_ADMIN
{
data
.
MainNavLinks
=
append
(
data
.
MainNavLinks
,
&
dtos
.
NavLink
{
data
.
MainNavLinks
=
append
(
data
.
MainNavLinks
,
&
dtos
.
NavLink
{
Text
:
"Data Sources"
,
Text
:
"Data Sources"
,
...
...
pkg/models/dashboard_snapshot.go
View file @
f1889003
...
@@ -20,6 +20,22 @@ type DashboardSnapshot struct {
...
@@ -20,6 +20,22 @@ type DashboardSnapshot struct {
Dashboard
map
[
string
]
interface
{}
Dashboard
map
[
string
]
interface
{}
}
}
// DashboardSnapshotDTO without dashboard map
type
DashboardSnapshotDTO
struct
{
Id
int64
`json:"id"`
Name
string
`json:"name"`
Key
string
`json:"key"`
DeleteKey
string
`json:"deleteKey"`
OrgId
int64
`json:"orgId"`
UserId
int64
`json:"userId"`
External
bool
`json:"external"`
ExternalUrl
string
`json:"externalUrl"`
Expires
time
.
Time
`json:"expires"`
Created
time
.
Time
`json:"created"`
Updated
time
.
Time
`json:"updated"`
}
// -----------------
// -----------------
// COMMANDS
// COMMANDS
...
@@ -48,3 +64,13 @@ type GetDashboardSnapshotQuery struct {
...
@@ -48,3 +64,13 @@ type GetDashboardSnapshotQuery struct {
Result
*
DashboardSnapshot
Result
*
DashboardSnapshot
}
}
type
DashboardSnapshots
[]
*
DashboardSnapshot
type
GetDashboardSnapshotsQuery
struct
{
Name
string
Limit
int
OrgId
int64
Result
DashboardSnapshots
}
pkg/models/dashboards.go
View file @
f1889003
...
@@ -34,6 +34,7 @@ type Dashboard struct {
...
@@ -34,6 +34,7 @@ type Dashboard struct {
Updated
time
.
Time
Updated
time
.
Time
UpdatedBy
int64
UpdatedBy
int64
CreatedBy
int64
Title
string
Title
string
Data
map
[
string
]
interface
{}
Data
map
[
string
]
interface
{}
...
@@ -91,8 +92,11 @@ func NewDashboardFromJson(data map[string]interface{}) *Dashboard {
...
@@ -91,8 +92,11 @@ func NewDashboardFromJson(data map[string]interface{}) *Dashboard {
// GetDashboardModel turns the command into the savable model
// GetDashboardModel turns the command into the savable model
func
(
cmd
*
SaveDashboardCommand
)
GetDashboardModel
()
*
Dashboard
{
func
(
cmd
*
SaveDashboardCommand
)
GetDashboardModel
()
*
Dashboard
{
dash
:=
NewDashboardFromJson
(
cmd
.
Dashboard
)
dash
:=
NewDashboardFromJson
(
cmd
.
Dashboard
)
if
dash
.
Data
[
"version"
]
==
0
{
dash
.
CreatedBy
=
cmd
.
UserId
}
dash
.
UpdatedBy
=
cmd
.
UserId
dash
.
OrgId
=
cmd
.
OrgId
dash
.
OrgId
=
cmd
.
OrgId
dash
.
UpdatedBy
=
cmd
.
UpdatedBy
dash
.
UpdateSlug
()
dash
.
UpdateSlug
()
return
dash
return
dash
}
}
...
@@ -114,9 +118,9 @@ func (dash *Dashboard) UpdateSlug() {
...
@@ -114,9 +118,9 @@ func (dash *Dashboard) UpdateSlug() {
type
SaveDashboardCommand
struct
{
type
SaveDashboardCommand
struct
{
Dashboard
map
[
string
]
interface
{}
`json:"dashboard" binding:"Required"`
Dashboard
map
[
string
]
interface
{}
`json:"dashboard" binding:"Required"`
Overwrite
bool
`json:"overwrite
"`
UserId
int64
`json:"userId
"`
OrgId
int64
`json:"-"`
OrgId
int64
`json:"-"`
UpdatedBy
int64
`json:"-
"`
Overwrite
bool
`json:"overwrite
"`
Result
*
Dashboard
Result
*
Dashboard
}
}
...
...
pkg/services/sqlstore/dashboard_snapshot.go
View file @
f1889003
...
@@ -12,6 +12,7 @@ func init() {
...
@@ -12,6 +12,7 @@ func init() {
bus
.
AddHandler
(
"sql"
,
CreateDashboardSnapshot
)
bus
.
AddHandler
(
"sql"
,
CreateDashboardSnapshot
)
bus
.
AddHandler
(
"sql"
,
GetDashboardSnapshot
)
bus
.
AddHandler
(
"sql"
,
GetDashboardSnapshot
)
bus
.
AddHandler
(
"sql"
,
DeleteDashboardSnapshot
)
bus
.
AddHandler
(
"sql"
,
DeleteDashboardSnapshot
)
bus
.
AddHandler
(
"sql"
,
SearchDashboardSnapshots
)
}
}
func
CreateDashboardSnapshot
(
cmd
*
m
.
CreateDashboardSnapshotCommand
)
error
{
func
CreateDashboardSnapshot
(
cmd
*
m
.
CreateDashboardSnapshotCommand
)
error
{
...
@@ -64,3 +65,18 @@ func GetDashboardSnapshot(query *m.GetDashboardSnapshotQuery) error {
...
@@ -64,3 +65,18 @@ func GetDashboardSnapshot(query *m.GetDashboardSnapshotQuery) error {
query
.
Result
=
&
snapshot
query
.
Result
=
&
snapshot
return
nil
return
nil
}
}
func
SearchDashboardSnapshots
(
query
*
m
.
GetDashboardSnapshotsQuery
)
error
{
var
snapshots
=
make
(
m
.
DashboardSnapshots
,
0
)
sess
:=
x
.
Limit
(
query
.
Limit
)
if
query
.
Name
!=
""
{
sess
.
Where
(
"name LIKE ?"
,
query
.
Name
)
}
sess
.
Where
(
"org_id = ?"
,
query
.
OrgId
)
err
:=
sess
.
Find
(
&
snapshots
)
query
.
Result
=
snapshots
return
err
}
pkg/services/sqlstore/migrations/dashboard_mig.go
View file @
f1889003
...
@@ -97,4 +97,9 @@ func addDashboardMigration(mg *Migrator) {
...
@@ -97,4 +97,9 @@ func addDashboardMigration(mg *Migrator) {
mg
.
AddMigration
(
"Add column updated_by in dashboard - v2"
,
NewAddColumnMigration
(
dashboardV2
,
&
Column
{
mg
.
AddMigration
(
"Add column updated_by in dashboard - v2"
,
NewAddColumnMigration
(
dashboardV2
,
&
Column
{
Name
:
"updated_by"
,
Type
:
DB_Int
,
Nullable
:
true
,
Name
:
"updated_by"
,
Type
:
DB_Int
,
Nullable
:
true
,
}))
}))
// add column to store creator of a dashboard
mg
.
AddMigration
(
"Add column created_by in dashboard - v2"
,
NewAddColumnMigration
(
dashboardV2
,
&
Column
{
Name
:
"created_by"
,
Type
:
DB_Int
,
Nullable
:
true
,
}))
}
}
public/app/core/routes/all.js
View file @
f1889003
...
@@ -137,6 +137,11 @@ define([
...
@@ -137,6 +137,11 @@ define([
templateUrl
:
'public/app/partials/reset_password.html'
,
templateUrl
:
'public/app/partials/reset_password.html'
,
controller
:
'ResetPasswordCtrl'
,
controller
:
'ResetPasswordCtrl'
,
})
})
.
when
(
'/dashboard/snapshots'
,
{
templateUrl
:
'app/features/snapshot/partials/snapshots.html'
,
controller
:
'SnapshotsCtrl'
,
controllerAs
:
'ctrl'
,
})
.
when
(
'/apps'
,
{
.
when
(
'/apps'
,
{
templateUrl
:
'public/app/features/apps/partials/list.html'
,
templateUrl
:
'public/app/features/apps/partials/list.html'
,
controller
:
'AppListCtrl'
,
controller
:
'AppListCtrl'
,
...
...
public/app/features/all.js
View file @
f1889003
...
@@ -5,6 +5,7 @@ define([
...
@@ -5,6 +5,7 @@ define([
'./templating/templateSrv'
,
'./templating/templateSrv'
,
'./dashboard/all'
,
'./dashboard/all'
,
'./playlist/all'
,
'./playlist/all'
,
'./snapshot/all'
,
'./panel/all'
,
'./panel/all'
,
'./profile/profileCtrl'
,
'./profile/profileCtrl'
,
'./profile/changePasswordCtrl'
,
'./profile/changePasswordCtrl'
,
...
...
public/app/features/dashboard/partials/settings.html
View file @
f1889003
...
@@ -115,9 +115,9 @@
...
@@ -115,9 +115,9 @@
</div>
</div>
<div
ng-if=
"editor.index == 4"
>
<div
ng-if=
"editor.index == 4"
>
<div
class=
"
editor-
row"
>
<div
class=
"row"
>
<
div
class=
"tight-form-section"
>
<
h5>
Dashboard info
</h5
>
<h5>
Dashboard info
</h5
>
<div
class=
"pull-left tight-form"
>
<div
class=
"tight-form"
>
<div
class=
"tight-form"
>
<ul
class=
"tight-form-list"
>
<ul
class=
"tight-form-list"
>
<li
class=
"tight-form-item"
style=
"width: 120px"
>
<li
class=
"tight-form-item"
style=
"width: 120px"
>
...
@@ -132,21 +132,43 @@
...
@@ -132,21 +132,43 @@
<div
class=
"tight-form"
>
<div
class=
"tight-form"
>
<ul
class=
"tight-form-list"
>
<ul
class=
"tight-form-list"
>
<li
class=
"tight-form-item"
style=
"width: 120px"
>
<li
class=
"tight-form-item"
style=
"width: 120px"
>
Last updated by:
</li>
<li
class=
"tight-form-item"
style=
"width: 180px"
>
{{dashboardMeta.updatedBy}}
</li>
</ul>
<div
class=
"clearfix"
></div>
</div>
<div
class=
"tight-form"
>
<ul
class=
"tight-form-list"
>
<li
class=
"tight-form-item"
style=
"width: 120px"
>
Created at:
Created at:
</li>
</li>
<li
class=
"tight-form-item"
style=
"width: 180px"
>
<li
class=
"tight-form-item"
style=
"width: 180px"
>
{{formatDate(dashboardMeta.created)}}
{{formatDate(dashboardMeta.created)}}
</li>
</li>
</ul>
</ul>
<div
class=
"clearfix"
></div>
<div
class=
"clearfix"
></div>
</div>
</div>
<div
class=
"tight-form
last
"
>
<div
class=
"tight-form"
>
<ul
class=
"tight-form-list"
>
<ul
class=
"tight-form-list"
>
<li
class=
"tight-form-item"
style=
"width: 120px"
>
<li
class=
"tight-form-item"
style=
"width: 120px"
>
Last upd
ated by:
Cre
ated by:
</li>
</li>
<li
class=
"tight-form-item"
style=
"width: 180px"
>
<li
class=
"tight-form-item"
style=
"width: 180px"
>
{{dashboardMeta.updatedBy}}
{{dashboardMeta.createdBy}}
</li>
</ul>
<div
class=
"clearfix"
></div>
</div>
<div
class=
"tight-form"
>
<ul
class=
"tight-form-list"
>
<li
class=
"tight-form-item"
style=
"width: 120px"
>
Current version:
</li>
<li
class=
"tight-form-item"
style=
"width: 180px"
>
{{dashboardMeta.version}}
</li>
</li>
</ul>
</ul>
<div
class=
"clearfix"
></div>
<div
class=
"clearfix"
></div>
...
...
public/app/features/snapshot/all.ts
0 → 100644
View file @
f1889003
import
'./snapshot_ctrl'
;
public/app/features/snapshot/partials/snapshots.html
0 → 100644
View file @
f1889003
<navbar
icon=
"fa fa-fw fa-camera-retro"
title=
"Dashboard snapshots"
></navbar>
<div
class=
"page-container"
>
<div
class=
"page-wide"
>
<h2>
Available snapshots
</h2>
<table
class=
"filter-table"
style=
"margin-top: 20px"
>
<thead>
<th><strong>
Name
</strong></th>
<th><strong>
Snapshot url
</strong></th>
<th
style=
"width: 70px"
></th>
<th
style=
"width: 25px"
></th>
</thead>
<tr
ng-repeat=
"snapshot in ctrl.snapshots"
>
<td>
<a
href=
"dashboard/snapshot/{{snapshot.key}}"
>
{{snapshot.name}}
</a>
</td>
<td
>
<a
href=
"dashboard/snapshot/{{snapshot.key}}"
>
dashboard/snapshot/{{snapshot.key}}
</a>
</td>
<td
class=
"text-center"
>
<a
href=
"dashboard/snapshot/{{snapshot.key}}"
class=
"btn btn-inverse btn-mini"
>
<i
class=
"fa fa-eye"
></i>
View
</a>
</td>
<td
class=
"text-right"
>
<a
ng-click=
"ctrl.removeSnapshot(snapshot)"
class=
"btn btn-danger btn-mini"
>
<i
class=
"fa fa-remove"
></i>
</a>
</td>
</tr>
</table>
</div>
</div>
public/app/features/snapshot/snapshot_ctrl.ts
0 → 100644
View file @
f1889003
///<reference path="../../headers/common.d.ts" />
import
angular
from
'angular'
;
import
_
from
'lodash'
;
export
class
SnapshotsCtrl
{
snapshots
:
any
;
/** @ngInject */
constructor
(
private
$rootScope
,
private
backendSrv
)
{
this
.
backendSrv
.
get
(
'/api/dashboard/snapshots'
).
then
(
result
=>
{
this
.
snapshots
=
result
;
});
}
removeSnapshotConfirmed
(
snapshot
)
{
_
.
remove
(
this
.
snapshots
,
{
key
:
snapshot
.
key
});
this
.
backendSrv
.
get
(
'/api/snapshots-delete/'
+
snapshot
.
deleteKey
)
.
then
(()
=>
{
this
.
$rootScope
.
appEvent
(
'alert-success'
,
[
'Snapshot deleted'
,
''
]);
},
()
=>
{
this
.
$rootScope
.
appEvent
(
'alert-error'
,
[
'Unable to delete snapshot'
,
''
]);
this
.
snapshots
.
push
(
snapshot
);
});
}
removeSnapshot
(
snapshot
)
{
this
.
$rootScope
.
appEvent
(
'confirm-modal'
,
{
title
:
'Confirm delete snapshot'
,
text
:
'Are you sure you want to delete snapshot '
+
snapshot
.
name
+
'?'
,
yesText
:
"Delete"
,
icon
:
"fa-warning"
,
onConfirm
:
()
=>
{
this
.
removeSnapshotConfirmed
(
snapshot
);
}
});
}
}
angular
.
module
(
'grafana.controllers'
).
controller
(
'SnapshotsCtrl'
,
SnapshotsCtrl
);
public/app/plugins/datasource/cloudwatch/datasource.js
View file @
f1889003
...
@@ -90,18 +90,20 @@ function (angular, _, moment, dateMath) {
...
@@ -90,18 +90,20 @@ function (angular, _, moment, dateMath) {
return
this
.
awsRequest
({
action
:
'__GetNamespaces'
});
return
this
.
awsRequest
({
action
:
'__GetNamespaces'
});
};
};
this
.
getMetrics
=
function
(
namespace
)
{
this
.
getMetrics
=
function
(
namespace
,
region
)
{
return
this
.
awsRequest
({
return
this
.
awsRequest
({
action
:
'__GetMetrics'
,
action
:
'__GetMetrics'
,
region
:
region
,
parameters
:
{
parameters
:
{
namespace
:
templateSrv
.
replace
(
namespace
)
namespace
:
templateSrv
.
replace
(
namespace
)
}
}
});
});
};
};
this
.
getDimensionKeys
=
function
(
namespace
)
{
this
.
getDimensionKeys
=
function
(
namespace
,
region
)
{
return
this
.
awsRequest
({
return
this
.
awsRequest
({
action
:
'__GetDimensions'
,
action
:
'__GetDimensions'
,
region
:
region
,
parameters
:
{
parameters
:
{
namespace
:
templateSrv
.
replace
(
namespace
)
namespace
:
templateSrv
.
replace
(
namespace
)
}
}
...
@@ -164,14 +166,14 @@ function (angular, _, moment, dateMath) {
...
@@ -164,14 +166,14 @@ function (angular, _, moment, dateMath) {
return
this
.
getNamespaces
();
return
this
.
getNamespaces
();
}
}
var
metricNameQuery
=
query
.
match
(
/^metrics
\(([^\)]
+
?)\)
/
);
var
metricNameQuery
=
query
.
match
(
/^metrics
\(([^\)]
+
?)
(
,
\s?([^
,
]
+
?))?
\)
/
);
if
(
metricNameQuery
)
{
if
(
metricNameQuery
)
{
return
this
.
getMetrics
(
metricNameQuery
[
1
]);
return
this
.
getMetrics
(
metricNameQuery
[
1
]
,
metricNameQuery
[
3
]
);
}
}
var
dimensionKeysQuery
=
query
.
match
(
/^dimension_keys
\(([^\)]
+
?)\)
/
);
var
dimensionKeysQuery
=
query
.
match
(
/^dimension_keys
\(([^\)]
+
?)
(
,
\s?([^
,
]
+
?))?
\)
/
);
if
(
dimensionKeysQuery
)
{
if
(
dimensionKeysQuery
)
{
return
this
.
getDimensionKeys
(
dimensionKeysQuery
[
1
]);
return
this
.
getDimensionKeys
(
dimensionKeysQuery
[
1
]
,
dimensionKeysQuery
[
3
]
);
}
}
var
dimensionValuesQuery
=
query
.
match
(
/^dimension_values
\(([^
,
]
+
?)
,
\s?([^
,
]
+
?)
,
\s?([^
,
]
+
?)
,
\s?([^
,
]
+
?)\)
/
);
var
dimensionValuesQuery
=
query
.
match
(
/^dimension_values
\(([^
,
]
+
?)
,
\s?([^
,
]
+
?)
,
\s?([^
,
]
+
?)
,
\s?([^
,
]
+
?)\)
/
);
...
...
public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.js
View file @
f1889003
...
@@ -102,7 +102,7 @@ function (angular, _) {
...
@@ -102,7 +102,7 @@ function (angular, _) {
var
query
=
$q
.
when
([]);
var
query
=
$q
.
when
([]);
if
(
segment
.
type
===
'key'
||
segment
.
type
===
'plus-button'
)
{
if
(
segment
.
type
===
'key'
||
segment
.
type
===
'plus-button'
)
{
query
=
$scope
.
datasource
.
getDimensionKeys
(
$scope
.
target
.
namespace
);
query
=
$scope
.
datasource
.
getDimensionKeys
(
$scope
.
target
.
namespace
,
$scope
.
target
.
region
);
}
else
if
(
segment
.
type
===
'value'
)
{
}
else
if
(
segment
.
type
===
'value'
)
{
var
dimensionKey
=
$scope
.
dimSegments
[
$index
-
2
].
value
;
var
dimensionKey
=
$scope
.
dimSegments
[
$index
-
2
].
value
;
query
=
$scope
.
datasource
.
getDimensionValues
(
target
.
region
,
target
.
namespace
,
target
.
metricName
,
dimensionKey
,
{});
query
=
$scope
.
datasource
.
getDimensionValues
(
target
.
region
,
target
.
namespace
,
target
.
metricName
,
dimensionKey
,
{});
...
@@ -160,7 +160,7 @@ function (angular, _) {
...
@@ -160,7 +160,7 @@ function (angular, _) {
};
};
$scope
.
getMetrics
=
function
()
{
$scope
.
getMetrics
=
function
()
{
return
$scope
.
datasource
.
metricFindQuery
(
'metrics('
+
$scope
.
target
.
namespace
+
')'
)
return
$scope
.
datasource
.
metricFindQuery
(
'metrics('
+
$scope
.
target
.
namespace
+
'
,'
+
$scope
.
target
.
region
+
'
)'
)
.
then
(
$scope
.
transformToSegments
(
true
));
.
then
(
$scope
.
transformToSegments
(
true
));
};
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment