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.
Files changed (49) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +8 -3
  3. data/lib/_logging.rb_ +55 -0
  4. data/lib/grafana.rb +1 -1
  5. data/lib/grafana/admin.rb +2 -1
  6. data/lib/grafana/alerts.rb +338 -14
  7. data/lib/grafana/annotations.rb +284 -9
  8. data/lib/grafana/auth.rb +150 -0
  9. data/lib/grafana/client.rb +81 -5
  10. data/lib/grafana/dashboard.rb +99 -7
  11. data/lib/grafana/dashboard_permissions.rb +132 -0
  12. data/lib/grafana/dashboard_versions.rb +101 -5
  13. data/lib/grafana/datasource.rb +34 -38
  14. data/lib/grafana/folder.rb +198 -0
  15. data/lib/grafana/folder_and_dashboard_search.rb +57 -0
  16. data/lib/grafana/folder_permissions.rb +155 -0
  17. data/lib/grafana/logging.rb +55 -0
  18. data/lib/grafana/login.rb +93 -49
  19. data/lib/grafana/network.rb +130 -101
  20. data/lib/grafana/organization.rb +2 -1
  21. data/lib/grafana/organizations.rb +15 -6
  22. data/lib/grafana/playlist.rb +594 -0
  23. data/lib/grafana/preferences.rb +122 -0
  24. data/lib/grafana/tags.rb +16 -0
  25. data/lib/grafana/teams.rb +364 -0
  26. data/lib/grafana/tools.rb +42 -9
  27. data/lib/grafana/user.rb +6 -2
  28. data/lib/grafana/users.rb +19 -11
  29. data/lib/grafana/validator.rb +47 -2
  30. data/lib/grafana/version.rb +3 -3
  31. metadata +16 -39
  32. data/doc/Array.html +0 -200
  33. data/doc/Boolean.html +0 -122
  34. data/doc/FalseClass.html +0 -132
  35. data/doc/Grafana.html +0 -172
  36. data/doc/Hash.html +0 -212
  37. data/doc/Logging.html +0 -326
  38. data/doc/Object.html +0 -286
  39. data/doc/Time.html +0 -200
  40. data/doc/TrueClass.html +0 -132
  41. data/doc/_index.html +0 -380
  42. data/doc/class_list.html +0 -51
  43. data/doc/file.README.html +0 -117
  44. data/doc/file_list.html +0 -56
  45. data/doc/frames.html +0 -17
  46. data/doc/index.html +0 -117
  47. data/doc/method_list.html +0 -747
  48. data/doc/top-level-namespace.html +0 -112
  49. 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
- begin
33
- @api_instance = RestClient::Resource.new(
34
- @url,
35
- timeout: @timeout.to_i,
36
- open_timeout: @open_timeout.to_i,
37
- headers: @http_headers,
38
- verify_ssl: false
39
- )
40
- rescue => e
41
- logger.error( e ) if @debug
42
- logger.debug( e.backtrace.join("\n") ) if @debug
43
- false
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
- if( retried < max_retries )
74
- retried += 1
75
- logger.debug( format( 'cannot login, socket error (retry %d / %d)', retried, max_retries ) ) if @debug
76
- sleep( sleep_between_retries )
77
- retry
78
- else
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
- if( retried < max_retries )
88
- retried += 1
89
- logger.debug( format( 'cannot login, connection refused (retry %d / %d)', retried, max_retries ) ) if @debug
90
- sleep( sleep_between_retries )
91
- retry
92
- else
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
- if( retried < max_retries )
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
102
- else
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
- if( retried < max_retries )
108
- retried += 1
109
- logger.debug( format( 'cannot login, host unreachable (retry %d / %d)', retried, max_retries ) ) if @debug
110
- sleep( sleep_between_retries )
111
- retry
112
- else
113
- raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url )
114
- end
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
- get( endpoint )
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