grafana 0.8.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +8 -3
  3. data/lib/grafana/admin.rb +39 -65
  4. data/lib/grafana/alerts.rb +334 -14
  5. data/lib/grafana/annotations.rb +284 -9
  6. data/lib/grafana/client.rb +38 -1
  7. data/lib/grafana/dashboard.rb +182 -39
  8. data/lib/grafana/dashboard_permissions.rb +132 -0
  9. data/lib/grafana/dashboard_versions.rb +101 -5
  10. data/lib/grafana/datasource.rb +93 -49
  11. data/lib/grafana/folder.rb +198 -0
  12. data/lib/grafana/folder_and_dashboard_search.rb +57 -0
  13. data/lib/grafana/folder_permissions.rb +155 -0
  14. data/lib/grafana/login.rb +41 -35
  15. data/lib/grafana/network.rb +128 -91
  16. data/lib/grafana/organization.rb +65 -34
  17. data/lib/grafana/organizations.rb +119 -175
  18. data/lib/grafana/playlist.rb +599 -0
  19. data/lib/grafana/preferences.rb +122 -0
  20. data/lib/grafana/tags.rb +19 -8
  21. data/lib/grafana/teams.rb +364 -0
  22. data/lib/grafana/tools.rb +44 -12
  23. data/lib/grafana/user.rb +78 -39
  24. data/lib/grafana/users.rb +104 -53
  25. data/lib/grafana/validator.rb +47 -2
  26. data/lib/grafana/version.rb +3 -3
  27. metadata +13 -38
  28. data/doc/Array.html +0 -200
  29. data/doc/Boolean.html +0 -122
  30. data/doc/FalseClass.html +0 -132
  31. data/doc/Grafana.html +0 -172
  32. data/doc/Hash.html +0 -212
  33. data/doc/Logging.html +0 -326
  34. data/doc/Object.html +0 -286
  35. data/doc/Time.html +0 -200
  36. data/doc/TrueClass.html +0 -132
  37. data/doc/_index.html +0 -380
  38. data/doc/class_list.html +0 -51
  39. data/doc/file.README.html +0 -117
  40. data/doc/file_list.html +0 -56
  41. data/doc/frames.html +0 -17
  42. data/doc/index.html +0 -117
  43. data/doc/method_list.html +0 -771
  44. data/doc/top-level-namespace.html +0 -112
@@ -0,0 +1,198 @@
1
+ module Grafana
2
+
3
+ # http://docs.grafana.org/http_api/folder/#folder-api
4
+ #
5
+ module Folder
6
+
7
+ # Get all folders
8
+ # GET /api/folders
9
+ # Returns all folders that the authenticated user has permission to view.
10
+ #
11
+ # @example
12
+ # folders
13
+ #
14
+ # @return [Hash]
15
+ #
16
+ def folders
17
+
18
+ v, mv = version.values
19
+ return { 'status' => 404, 'message' => format( 'folder has been supported in Grafana since version 5. you use version %s', v) } if(mv < 5)
20
+
21
+ endpoint = '/api/folders'
22
+ @logger.debug("Getting all folders (GET #{endpoint})") if @debug
23
+ get(endpoint)
24
+ end
25
+
26
+ # Get folder by uid
27
+ # GET /api/folders/:uid
28
+ #
29
+ # Will return the folder given the folder uid.
30
+ #
31
+ # Get folder by id
32
+ # GET /api/folders/:id
33
+ #
34
+ # Will return the folder identified by id.
35
+ def folder( folder_uid )
36
+
37
+ raise ArgumentError.new(format('wrong type. user \'folder_uid\' must be an String (for an Folder Uid) or an Integer (for an Folder Id), given \'%s\'', folder_uid.class.to_s)) \
38
+ if( folder_uid.is_a?(String) && folder_uid.is_a?(Integer) )
39
+ raise ArgumentError.new('missing \'folder_uid\'') if( folder_uid.size.zero? )
40
+
41
+ v, mv = version.values
42
+ return { 'status' => 404, 'message' => format( 'folder has been supported in Grafana since version 5. you use version %s', v) } if(mv < 5)
43
+
44
+ if(folder_uid.is_a?(Integer))
45
+
46
+ f = folders
47
+ f = JSON.parse(f) if(f.is_a?(String))
48
+
49
+ status = f.dig('status')
50
+ return f if( status != 200 )
51
+
52
+ f = f.dig('message').detect {|x| x['id'] == folder_uid }
53
+
54
+ return { 'status' => 404, 'message' => format( 'No Folder \'%s\' found', folder_uid) } if( folder_uid.nil? )
55
+
56
+ folder_uid = f.dig('uid') unless(f.nil?)
57
+
58
+ return { 'status' => 404, 'message' => format( 'No Folder \'%s\' found', folder_uid) } if( folder_uid.is_a?(Integer) )
59
+ end
60
+
61
+ return { 'status' => 404, 'message' => format( 'The uid can have a maximum length of 40 characters, but it is %s characters long', folder_uid.length) } \
62
+ if( folder_uid.is_a?(String) && folder_uid.length > 40 )
63
+ return { 'status' => 404, 'message' => format( 'No Folder \'%s\' found', folder_uid) } if( folder_uid.nil? )
64
+
65
+ endpoint = format( '/api/folders/%s', folder_uid )
66
+
67
+ @logger.debug("Getting folder by Id #{folder_uid} (GET #{endpoint})") if @debug
68
+ get(endpoint)
69
+ end
70
+
71
+ # Create folder
72
+ # POST /api/folders
73
+ #
74
+ # Creates a new folder.
75
+ # JSON Body schema:
76
+ #
77
+ # uid - Optional unique identifier.
78
+ # title - The title of the folder.
79
+ def create_folder( params )
80
+
81
+ raise ArgumentError.new(format('wrong type. \'params\' must be an Hash, given \'%s\'', params.class.to_s)) unless( params.is_a?(Hash) )
82
+ raise ArgumentError.new('missing \'params\'') if( params.size.zero? )
83
+
84
+ v, mv = version.values
85
+ return { 'status' => 404, 'message' => format( 'folder has been supported in Grafana since version 5. you use version %s', v) } if(mv < 5)
86
+
87
+ title = validate( params, required: false, var: 'title', type: String )
88
+ uid = validate( params, required: true , var: 'uid' , type: String )
89
+
90
+ return { 'status' => 404, 'message' => format( 'The uid can have a maximum length of 40 characters. \'%s\' given', uid.length) } if( uid.length > 40 )
91
+
92
+ data = {
93
+ uid: uid,
94
+ title: title
95
+ }
96
+ data.reject!{ |_, y| y.nil? }
97
+
98
+ payload = data.deep_string_keys
99
+
100
+ endpoint = '/api/folders'
101
+
102
+ @logger.debug("create folder#{title} (GET #{endpoint})") if @debug
103
+ logger.debug(payload.to_json) if(@debug)
104
+
105
+ post( endpoint, payload.to_json )
106
+ end
107
+
108
+ # Update folder
109
+ # PUT /api/folders/:uid
110
+ #
111
+ # schould be fail, when the version are not incremented
112
+ # overwrite helps
113
+ #
114
+ # Updates an existing folder identified by uid.
115
+ # JSON Body schema:
116
+ #
117
+ # - uid - Provide another unique identifier than stored to change the unique identifier.
118
+ # - title - The title of the folder.
119
+ # - version - Provide the current version to be able to update the folder. Not needed if overwrite=true.
120
+ # - overwrite - Set to true if you want to overwrite existing folder with newer version.
121
+ def update_folder( params )
122
+
123
+ raise ArgumentError.new(format('wrong type. \'params\' must be an Hash, given \'%s\'', params.class.to_s)) unless( params.is_a?(Hash) )
124
+ raise ArgumentError.new('missing \'params\'') if( params.size.zero? )
125
+
126
+ v, mv = version.values
127
+ return { 'status' => 404, 'message' => format( 'folder has been supported in Grafana since version 5. you use version %s', v) } if(mv < 5)
128
+
129
+ uid = validate( params, required: true , var: 'uid' , type: String )
130
+ title = validate( params, required: true , var: 'title' , type: String )
131
+ new_uid = validate( params, required: false, var: 'new_uid' , type: String )
132
+ version = validate( params, required: false, var: 'version' , type: Integer )
133
+ overwrite = validate( params, required: false, var: 'overwrite', type: Boolean ) || false
134
+
135
+ existing_folder = folder( uid )
136
+ return { 'status' => 404, 'message' => format( 'No Folder \'%s\' found', uid) } if( existing_folder.dig('status') != 200 )
137
+
138
+ unless( new_uid.nil? )
139
+ existing_folder = folder( new_uid )
140
+ return { 'status' => 404, 'message' => format( 'Folder \'%s\' found', uid) } if( existing_folder.dig('status') == 200 )
141
+ end
142
+
143
+ payload = {
144
+ title: title,
145
+ uid: new_uid,
146
+ version: version,
147
+ overwrite: overwrite
148
+ }
149
+ payload.reject!{ |_, y| y.nil? }
150
+
151
+ @logger.debug("Updating folder with Uid #{uid}") if @debug
152
+
153
+ endpoint = format( '/api/folders/%s', uid )
154
+
155
+ put( endpoint, payload.to_json )
156
+ end
157
+
158
+
159
+ # Delete folder
160
+ # DELETE /api/folders/:uid
161
+ #
162
+ # Deletes an existing folder identified by uid together with all dashboards stored in the folder, if any.
163
+ # This operation cannot be reverted.
164
+ def delete_folder( folder_uid )
165
+
166
+ raise ArgumentError.new(format('wrong type. user \'folder_uid\' must be an String (for an Folder Uid) or an Integer (for an Folder Id), given \'%s\'', folder_uid.class.to_s)) \
167
+ if( folder_uid.is_a?(String) && folder_uid.is_a?(Integer) )
168
+ raise ArgumentError.new('missing \'folder_uid\'') if( folder_uid.size.zero? )
169
+
170
+ v, mv = version.values
171
+ return { 'status' => 404, 'message' => format( 'folder has been supported in Grafana since version 5. you use version %s', v) } if(mv < 5)
172
+
173
+ if(folder_uid.is_a?(Integer))
174
+
175
+ fldrs = folders
176
+
177
+ fldrs = JSON.parse(fldrs) if(fldrs.is_a?(String))
178
+ status = fldrs.dig('status')
179
+
180
+ return fldrs if( status != 200 )
181
+
182
+ fldrs.dig('message').each do |d|
183
+ folder_uid = d.dig('uid').to_s
184
+ end
185
+ end
186
+
187
+ return { 'status' => 404, 'message' => format( 'No User \'%s\' found', folder_uid) } if( folder_uid.nil? )
188
+
189
+ endpoint = format( '/api/folders/%s', folder_uid )
190
+
191
+ @logger.debug("deleting folder by uid #{folder_uid} (GET #{endpoint})") if @debug
192
+ delete(endpoint)
193
+ end
194
+
195
+
196
+ end
197
+ end
198
+
@@ -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
+
data/lib/grafana/login.rb CHANGED
@@ -67,58 +67,59 @@ module Grafana
67
67
  accept: 'application/json',
68
68
  cookies: response_cookies
69
69
  }
70
+ @username = username
71
+ @password = password
70
72
  end
71
73
 
72
74
  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
75
+ raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url ) unless( retried < max_retries )
76
+
77
+ retried += 1
78
+ logger.debug( format( 'cannot login, socket error (retry %d / %d)', retried, max_retries ) ) if @debug
79
+ sleep( sleep_between_retries )
80
+ retry
81
81
 
82
82
  rescue RestClient::Unauthorized
83
83
  logger.debug( request_data.to_json ) if @debug
84
84
  raise format( 'Not authorized to connect \'%s\' - wrong username or password?', @url )
85
85
 
86
86
  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
87
+ raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url ) unless( retried < max_retries )
88
+
89
+ retried += 1
90
+ logger.debug( format( 'cannot login, connection refused (retry %d / %d)', retried, max_retries ) ) if @debug
91
+ sleep( sleep_between_retries )
92
+ retry
95
93
 
96
94
  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
95
+ raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url ) unless( retried < max_retries )
96
+
97
+ retried += 1
98
+ logger.debug( format( 'cannot login, connection refused (retry %d / %d)', retried, max_retries ) ) if @debug
99
+ sleep( sleep_between_retries )
100
+ retry
105
101
 
106
102
  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
103
+ raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url ) unless( retried < max_retries )
104
+
105
+ retried += 1
106
+ logger.debug( format( 'cannot login, host unreachable (retry %d / %d)', retried, max_retries ) ) if @debug
107
+ sleep( sleep_between_retries )
108
+ retry
109
+
110
+ rescue => error
111
+ raise format( 'Maximum retries (%d) against \'%s/login\' reached. Giving up ...', max_retries, @url ) unless( retried < max_retries )
112
+
113
+ retried += 1
114
+ logger.error( error )
115
+ logger.debug( format( 'cannot login (retry %d / %d)', retried, max_retries ) ) if @debug
116
+ sleep( sleep_between_retries )
117
+ retry
115
118
  end
116
119
 
117
120
  logger.debug('User session initiated') if @debug
118
-
119
121
  return true
120
122
  end
121
-
122
123
  false
123
124
  end
124
125
 
@@ -130,10 +131,15 @@ module Grafana
130
131
  # @return [Hash]
131
132
  #
132
133
  def ping_session
133
- endpoint = '/api/login/ping'
134
134
  logger.debug( "Pinging current session (GET #{endpoint})" ) if @debug
135
+ endpoint = '/api/login/ping'
135
136
  get( endpoint )
136
137
  end
138
+
139
+
140
+ def headers
141
+ @headers
142
+ end
137
143
  end
138
144
 
139
145
  end