grafana 0.8.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +8 -3
- data/lib/_logging.rb_ +55 -0
- data/lib/grafana.rb +1 -1
- data/lib/grafana/admin.rb +2 -1
- data/lib/grafana/alerts.rb +338 -14
- data/lib/grafana/annotations.rb +284 -9
- data/lib/grafana/auth.rb +150 -0
- data/lib/grafana/client.rb +81 -5
- data/lib/grafana/dashboard.rb +99 -7
- data/lib/grafana/dashboard_permissions.rb +132 -0
- data/lib/grafana/dashboard_versions.rb +101 -5
- data/lib/grafana/datasource.rb +34 -38
- data/lib/grafana/folder.rb +198 -0
- data/lib/grafana/folder_and_dashboard_search.rb +57 -0
- data/lib/grafana/folder_permissions.rb +155 -0
- data/lib/grafana/logging.rb +55 -0
- data/lib/grafana/login.rb +93 -49
- data/lib/grafana/network.rb +130 -101
- data/lib/grafana/organization.rb +2 -1
- data/lib/grafana/organizations.rb +15 -6
- data/lib/grafana/playlist.rb +594 -0
- data/lib/grafana/preferences.rb +122 -0
- data/lib/grafana/tags.rb +16 -0
- data/lib/grafana/teams.rb +364 -0
- data/lib/grafana/tools.rb +42 -9
- data/lib/grafana/user.rb +6 -2
- data/lib/grafana/users.rb +19 -11
- data/lib/grafana/validator.rb +47 -2
- data/lib/grafana/version.rb +3 -3
- metadata +16 -39
- data/doc/Array.html +0 -200
- data/doc/Boolean.html +0 -122
- data/doc/FalseClass.html +0 -132
- data/doc/Grafana.html +0 -172
- data/doc/Hash.html +0 -212
- data/doc/Logging.html +0 -326
- data/doc/Object.html +0 -286
- data/doc/Time.html +0 -200
- data/doc/TrueClass.html +0 -132
- data/doc/_index.html +0 -380
- data/doc/class_list.html +0 -51
- data/doc/file.README.html +0 -117
- data/doc/file_list.html +0 -56
- data/doc/frames.html +0 -17
- data/doc/index.html +0 -117
- data/doc/method_list.html +0 -747
- data/doc/top-level-namespace.html +0 -112
- data/lib/logging.rb +0 -35
@@ -0,0 +1,57 @@
|
|
1
|
+
module Grafana
|
2
|
+
|
3
|
+
# http://docs.grafana.org/http_api/folder_dashboard_search/#folder-dashboard-search-api
|
4
|
+
#
|
5
|
+
module FolderSearch
|
6
|
+
|
7
|
+
# Search folders and dashboards
|
8
|
+
# GET /api/search/
|
9
|
+
#
|
10
|
+
# Query parameters:
|
11
|
+
#
|
12
|
+
# - query - Search Query
|
13
|
+
# - tag - List of tags to search for
|
14
|
+
# - type - Type to search for, dash-folder or dash-db
|
15
|
+
# - dashboardIds - List of dashboard id's to search for
|
16
|
+
# - folderIds - List of folder id's to search in for dashboards
|
17
|
+
# - starred - Flag indicating if only starred Dashboards should be returned
|
18
|
+
# - limit - Limit the number of returned results
|
19
|
+
def folder_and_dashboard_search(params)
|
20
|
+
|
21
|
+
raise ArgumentError.new(format('wrong type. \'params\' must be an Hash, given \'%s\'', params.class.to_s)) unless( params.is_a?(Hash) )
|
22
|
+
|
23
|
+
v, mv = version.values
|
24
|
+
return { 'status' => 404, 'message' => format( 'team has been supported in Grafana since version 5. you use version %s', v) } if(mv < 5)
|
25
|
+
|
26
|
+
query = validate( params, required: false, var: 'query' , type: String )
|
27
|
+
tag = validate( params, required: false, var: 'tag ' , type: String )
|
28
|
+
type = validate( params, required: false, var: 'type' , type: String )
|
29
|
+
dashboard_id = validate( params, required: false, var: 'dashboardIds', type: Integer )
|
30
|
+
folder_id = validate( params, required: false, var: 'folderIds' , type: Integer )
|
31
|
+
starred = validate( params, required: false, var: 'starred' , type: Boolean )
|
32
|
+
limit = validate( params, required: false, var: 'limit' , type: Integer )
|
33
|
+
|
34
|
+
unless(type.nil?)
|
35
|
+
valid_types = ['dash-folder', 'dash-db']
|
36
|
+
downcased = Set.new valid_types.map(&:downcase)
|
37
|
+
return { 'status' => 404, 'message' => format( 'wrong type. Must be one of %s, given \'%s\'', valid_types.join(', '), type ) } unless( downcased.include?( type.downcase ) )
|
38
|
+
end
|
39
|
+
|
40
|
+
api = []
|
41
|
+
api << format( 'query=%s', CGI.escape( query ) ) unless( query.nil? )
|
42
|
+
api << format( 'tags=%s', tag ) unless( tag.nil? )
|
43
|
+
api << format( 'type=%s', type ) unless( type.nil? )
|
44
|
+
api << format( 'dashboardId=%s', dashboard_id ) unless( dashboard_id.nil? )
|
45
|
+
api << format( 'folderId=%s', folder_id ) unless( folder_id.nil? )
|
46
|
+
api << format( 'starred=%s', starred ) unless( starred.nil? )
|
47
|
+
api << format( 'limit=%s', limit ) unless( limit.nil? )
|
48
|
+
|
49
|
+
api = api.join( '&' )
|
50
|
+
|
51
|
+
endpoint = format('/api/search?%s', api)
|
52
|
+
get(endpoint)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module Grafana
|
2
|
+
|
3
|
+
# http://docs.grafana.org/http_api/folder_permissions/#folder-permissions-api
|
4
|
+
#
|
5
|
+
# This API can be used to update/get the permissions for a folder.
|
6
|
+
#
|
7
|
+
# Permissions with folderId=-1 are the default permissions for users with the Viewer and Editor roles.
|
8
|
+
# Permissions can be set for a user, a team or a role (Viewer or Editor).
|
9
|
+
# Permissions cannot be set for Admins - they always have access to everything.
|
10
|
+
#
|
11
|
+
# The permission levels for the permission field:
|
12
|
+
#
|
13
|
+
# 1 = View
|
14
|
+
# 2 = Edit
|
15
|
+
# 4 = Admin
|
16
|
+
#
|
17
|
+
module FolderPermissions
|
18
|
+
|
19
|
+
# Get permissions for a folder
|
20
|
+
# GET /api/folders/:uid/permissions
|
21
|
+
#
|
22
|
+
# Gets all existing permissions for the folder with the given uid.
|
23
|
+
def folder_permissions( folder_id )
|
24
|
+
|
25
|
+
v, mv = version.values
|
26
|
+
return { 'status' => 404, 'message' => format( 'folder has been supported in Grafana since version 5. you use version %s', v) } if(mv < 5)
|
27
|
+
|
28
|
+
f = folder( folder_id )
|
29
|
+
|
30
|
+
status = f.dig('status')
|
31
|
+
return f if( status != 200 )
|
32
|
+
|
33
|
+
endpoint = format('/api/folders/%s/permissions', f.dig('uid') )
|
34
|
+
|
35
|
+
@logger.debug("Getting all folders (GET #{endpoint})") if @debug
|
36
|
+
get(endpoint)
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
# Update permissions for a folder
|
41
|
+
# POST /api/folders/:uid/permissions
|
42
|
+
#
|
43
|
+
# Updates permissions for a folder.
|
44
|
+
# This operation will remove existing permissions if they're not included in the request.
|
45
|
+
#
|
46
|
+
# JSON body schema:
|
47
|
+
#
|
48
|
+
# items - The permission items to add/update. Items that are omitted from the list will be removed.
|
49
|
+
#
|
50
|
+
def update_folder_permissions( params )
|
51
|
+
|
52
|
+
raise ArgumentError.new(format('wrong type. \'params\' must be an Hash, given \'%s\'', params.class.to_s)) unless( params.is_a?(Hash) )
|
53
|
+
raise ArgumentError.new('missing \'params\'') if( params.size.zero? )
|
54
|
+
|
55
|
+
v, mv = version.values
|
56
|
+
return { 'status' => 404, 'message' => format( 'folder has been supported in Grafana since version 5. you use version %s', v) } if(mv < 5)
|
57
|
+
|
58
|
+
folder = validate( params, required: true, var: 'folder' , type: String )
|
59
|
+
permissions = validate( params, required: true, var: 'permissions', type: Hash )
|
60
|
+
|
61
|
+
return { 'status' => 404, 'message' => 'no permissions given' } if( permissions.size.zero? )
|
62
|
+
|
63
|
+
f_folder = folder(folder)
|
64
|
+
return { 'status' => 404, 'message' => format( 'No Folder \'%s\' found', folder) } if( f_folder.dig('status') != 200 )
|
65
|
+
|
66
|
+
folder_uid = f_folder.dig('uid')
|
67
|
+
|
68
|
+
valid_roles = %w[View Edit Admin]
|
69
|
+
# valid_keys = %w[role permission teamId userId]
|
70
|
+
|
71
|
+
c_team = permissions.dig('team')
|
72
|
+
c_user = permissions.dig('user')
|
73
|
+
team = []
|
74
|
+
user = []
|
75
|
+
|
76
|
+
unless(c_team.nil?)
|
77
|
+
check_keys = []
|
78
|
+
|
79
|
+
c_team.uniq.each do |x|
|
80
|
+
k = x.keys.first
|
81
|
+
v = x.values.first
|
82
|
+
r = validate_hash( v, valid_roles )
|
83
|
+
|
84
|
+
f_team = team(k)
|
85
|
+
team_id = f_team.dig('id')
|
86
|
+
|
87
|
+
next unless(( f_team.dig('status') == 200) && !check_keys.include?(team_id) && r == true )
|
88
|
+
|
89
|
+
check_keys << team_id
|
90
|
+
|
91
|
+
role_id = valid_roles.index(v)
|
92
|
+
role_id += 1
|
93
|
+
role_id += 1 if(v == 'Admin')
|
94
|
+
|
95
|
+
team << {
|
96
|
+
teamId: team_id,
|
97
|
+
permission: role_id
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
unless(c_user.nil?)
|
103
|
+
check_keys = []
|
104
|
+
|
105
|
+
c_user.uniq.each do |x|
|
106
|
+
k = x.keys.first
|
107
|
+
v = x.values.first
|
108
|
+
r = validate_hash( v, valid_roles )
|
109
|
+
|
110
|
+
f_user = user(k)
|
111
|
+
user_id = f_user.dig('id')
|
112
|
+
|
113
|
+
next unless(( f_user.dig('status') == 200) && !check_keys.include?(user_id) && r == true )
|
114
|
+
|
115
|
+
check_keys << user_id
|
116
|
+
|
117
|
+
role_id = valid_roles.index(v)
|
118
|
+
role_id += 1
|
119
|
+
role_id += 1 if(v == 'Admin')
|
120
|
+
|
121
|
+
user << {
|
122
|
+
userId: user_id,
|
123
|
+
permission: role_id
|
124
|
+
}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
payload = {
|
129
|
+
items: team + user
|
130
|
+
}
|
131
|
+
payload.reject!{ |_, y| y.nil? }
|
132
|
+
|
133
|
+
endpoint = format( '/api/folders/%s/permissions', folder_uid )
|
134
|
+
|
135
|
+
post(endpoint, payload.to_json)
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
# private
|
140
|
+
# def validate_hash( value, valid_params )
|
141
|
+
#
|
142
|
+
# downcased = Set.new valid_params.map(&:downcase)
|
143
|
+
#
|
144
|
+
# unless( downcased.include?( value.downcase ) )
|
145
|
+
# return {
|
146
|
+
# 'status' => 404,
|
147
|
+
# 'message' => format( 'wrong permissions. Must be one of %s, given \'%s\'', valid_params.join(', '), value )
|
148
|
+
# }
|
149
|
+
# end
|
150
|
+
# true
|
151
|
+
# end
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
# -------------------------------------------------------------------------------------------------
|
5
|
+
|
6
|
+
module Logging
|
7
|
+
|
8
|
+
def logger
|
9
|
+
@logger ||= Logging.logger_for( self.class.name )
|
10
|
+
end
|
11
|
+
|
12
|
+
# Use a hash class-ivar to cache a unique Logger per class:
|
13
|
+
@loggers = {}
|
14
|
+
|
15
|
+
class << self
|
16
|
+
|
17
|
+
def logger_for( classname )
|
18
|
+
@loggers[classname] ||= configure_logger_for( classname )
|
19
|
+
end
|
20
|
+
|
21
|
+
def configure_logger_for( classname )
|
22
|
+
|
23
|
+
log_level = ENV.fetch('LOG_LEVEL', 'INFO' )
|
24
|
+
level = log_level.dup
|
25
|
+
|
26
|
+
# DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
|
27
|
+
log_level = case level.upcase
|
28
|
+
when 'DEBUG'
|
29
|
+
Logger::DEBUG # Low-level information for developers.
|
30
|
+
when 'INFO'
|
31
|
+
Logger::INFO # Generic (useful) information about system operation.
|
32
|
+
when 'WARN'
|
33
|
+
Logger::WARN # A warning.
|
34
|
+
when 'ERROR'
|
35
|
+
Logger::ERROR # A handleable error condition.
|
36
|
+
when 'FATAL'
|
37
|
+
Logger::FATAL # An unhandleable error that results in a program crash.
|
38
|
+
else
|
39
|
+
Logger::UNKNOWN # An unknown message that should always be logged.
|
40
|
+
end
|
41
|
+
|
42
|
+
$stdout.sync = true
|
43
|
+
logger = Logger.new($stdout)
|
44
|
+
logger.level = log_level
|
45
|
+
logger.datetime_format = "%Y-%m-%d %H:%M:%S %z"
|
46
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
47
|
+
"[#{datetime.strftime( logger.datetime_format )}] #{severity.ljust(5)} #{msg}\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
logger
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# -------------------------------------------------------------------------------------------------
|
data/lib/grafana/login.rb
CHANGED
@@ -29,20 +29,27 @@ module Grafana
|
|
29
29
|
max_retries = validate( params, required: false, var: 'max_retries', type: Integer ) || 2
|
30
30
|
sleep_between_retries = validate( params, required: false, var: 'sleep_between_retries', type: Integer ) || 5
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
32
|
+
# raise 'no valid rest instance found' if( @api_instance.nil? )
|
33
|
+
|
34
|
+
# only useful for Grafana version < 6
|
35
|
+
#
|
36
|
+
if( @api_instance.nil? )
|
37
|
+
|
38
|
+
@api_user = username
|
39
|
+
@api_password = password
|
40
|
+
|
41
|
+
@api_instance = create_instance
|
42
|
+
|
43
|
+
return true if(@api_instance)
|
44
|
+
else
|
45
|
+
logger.debug('User session already initiated') if @debug
|
46
|
+
return true
|
44
47
|
end
|
45
48
|
|
49
|
+
logger.debug( "resource: #{@api_instance.inspect}")
|
50
|
+
|
51
|
+
false
|
52
|
+
|
46
53
|
request_data = { 'User' => username, 'Password' => password }
|
47
54
|
|
48
55
|
if( @api_instance )
|
@@ -51,13 +58,15 @@ module Grafana
|
|
51
58
|
@headers = {}
|
52
59
|
|
53
60
|
begin
|
54
|
-
logger.debug('Attempting to establish user session') if @debug
|
61
|
+
logger.debug('Attempting to establish user session') # if @debug
|
55
62
|
|
56
|
-
response = @api_instance['/login'].post(
|
63
|
+
response = @api_instance['/api/login'].post(
|
57
64
|
request_data.to_json,
|
58
65
|
content_type: 'application/json; charset=UTF-8'
|
59
66
|
)
|
60
67
|
|
68
|
+
logger.debug( "response #{response}")
|
69
|
+
|
61
70
|
response_cookies = response.cookies
|
62
71
|
response_code = response.code.to_i
|
63
72
|
|
@@ -67,58 +76,59 @@ module Grafana
|
|
67
76
|
accept: 'application/json',
|
68
77
|
cookies: response_cookies
|
69
78
|
}
|
79
|
+
@username = username
|
80
|
+
@password = password
|
70
81
|
end
|
71
82
|
|
72
83
|
rescue SocketError
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url )
|
80
|
-
end
|
84
|
+
raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url ) unless( retried < max_retries )
|
85
|
+
|
86
|
+
retried += 1
|
87
|
+
logger.debug( format( 'cannot login, socket error (retry %d / %d)', retried, max_retries ) ) if @debug
|
88
|
+
sleep( sleep_between_retries )
|
89
|
+
retry
|
81
90
|
|
82
91
|
rescue RestClient::Unauthorized
|
83
92
|
logger.debug( request_data.to_json ) if @debug
|
84
93
|
raise format( 'Not authorized to connect \'%s\' - wrong username or password?', @url )
|
85
94
|
|
86
95
|
rescue RestClient::BadGateway
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url )
|
94
|
-
end
|
96
|
+
raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url ) unless( retried < max_retries )
|
97
|
+
|
98
|
+
retried += 1
|
99
|
+
logger.debug( format( 'cannot login, connection refused (retry %d / %d)', retried, max_retries ) ) if @debug
|
100
|
+
sleep( sleep_between_retries )
|
101
|
+
retry
|
95
102
|
|
96
103
|
rescue Errno::ECONNREFUSED
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url )
|
104
|
-
end
|
104
|
+
raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url ) unless( retried < max_retries )
|
105
|
+
|
106
|
+
retried += 1
|
107
|
+
logger.debug( format( 'cannot login, connection refused (retry %d / %d)', retried, max_retries ) ) if @debug
|
108
|
+
sleep( sleep_between_retries )
|
109
|
+
retry
|
105
110
|
|
106
111
|
rescue Errno::EHOSTUNREACH
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
112
|
+
raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url ) unless( retried < max_retries )
|
113
|
+
|
114
|
+
retried += 1
|
115
|
+
logger.debug( format( 'cannot login, host unreachable (retry %d / %d)', retried, max_retries ) ) if @debug
|
116
|
+
sleep( sleep_between_retries )
|
117
|
+
retry
|
118
|
+
|
119
|
+
rescue => error
|
120
|
+
raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url ) unless( retried < max_retries )
|
121
|
+
|
122
|
+
retried += 1
|
123
|
+
logger.error( error )
|
124
|
+
logger.debug( format( 'cannot login (retry %d / %d)', retried, max_retries ) ) if @debug
|
125
|
+
sleep( sleep_between_retries )
|
126
|
+
retry
|
115
127
|
end
|
116
128
|
|
117
129
|
logger.debug('User session initiated') if @debug
|
118
|
-
|
119
130
|
return true
|
120
131
|
end
|
121
|
-
|
122
132
|
false
|
123
133
|
end
|
124
134
|
|
@@ -132,7 +142,41 @@ module Grafana
|
|
132
142
|
def ping_session
|
133
143
|
endpoint = '/api/login/ping'
|
134
144
|
logger.debug( "Pinging current session (GET #{endpoint})" ) if @debug
|
135
|
-
|
145
|
+
|
146
|
+
begin
|
147
|
+
get( endpoint )
|
148
|
+
rescue => error
|
149
|
+
logger.error(e)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Returns health information about Grafana
|
154
|
+
#
|
155
|
+
# @example
|
156
|
+
# health
|
157
|
+
#
|
158
|
+
# @return [Hash]
|
159
|
+
#
|
160
|
+
def health
|
161
|
+
endpoint = '/api/health'
|
162
|
+
logger.debug( "get health information (GET #{endpoint})" ) if @debug
|
163
|
+
|
164
|
+
result = get( endpoint )
|
165
|
+
|
166
|
+
if( result.is_a?(Hash) )
|
167
|
+
status = result.dig('status')
|
168
|
+
if( status.to_i == 200 )
|
169
|
+
message = result.dig('message')
|
170
|
+
r = message.gsub('=>', ':').gsub(':nil,', ':null,')
|
171
|
+
return { 'status' => status, 'message' => JSON.parse( r ) }
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
result
|
176
|
+
end
|
177
|
+
|
178
|
+
def headers
|
179
|
+
@headers
|
136
180
|
end
|
137
181
|
end
|
138
182
|
|