grafana 0.8.2

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.
@@ -0,0 +1,40 @@
1
+
2
+ module Grafana
3
+
4
+ # http://docs.grafana.org/http_api/annotations/
5
+ #
6
+ module Annotations
7
+
8
+ # Find Annotations
9
+ # http://docs.grafana.org/http_api/annotations/#find-annotations
10
+ # GET /api/annotations?from=1506676478816&to=1507281278816&tags=tag1&tags=tag2&limit=100
11
+ def find_annotation( params ); end
12
+
13
+ # Create Annotation
14
+ # http://docs.grafana.org/http_api/annotations/#create-annotation
15
+ # POST /api/annotations
16
+ def create_annotation( params ); end
17
+
18
+ # Create Annotation in Graphite format
19
+ # http://docs.grafana.org/http_api/annotations/#create-annotation-in-graphite-format
20
+ # POST /api/annotations/graphite
21
+ def create_annotation_graphite( params ); end
22
+
23
+ # Update Annotation
24
+ # http://docs.grafana.org/http_api/annotations/#update-annotation
25
+ # PUT /api/annotations/:id
26
+ def update_annotation( params ); end
27
+
28
+ # Delete Annotation By Id
29
+ # http://docs.grafana.org/http_api/annotations/#delete-annotation-by-id
30
+ # DELETE /api/annotation/:id
31
+ def delete_annotation( params ); end
32
+
33
+ # Delete Annotation By RegionId
34
+ # http://docs.grafana.org/http_api/annotations/#delete-annotation-by-regionid
35
+ # DELETE /api/annotation/region/:id
36
+ def delete_annotation_by_region( params ); end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,126 @@
1
+
2
+ require 'ruby_dig' if RUBY_VERSION < '2.3'
3
+
4
+ require 'rest-client'
5
+ require 'json'
6
+ require 'timeout'
7
+
8
+ require_relative 'version'
9
+ require_relative 'validator'
10
+ require_relative 'login'
11
+ require_relative 'network'
12
+ require_relative 'tools'
13
+ require_relative 'admin'
14
+ require_relative 'annotations'
15
+ require_relative 'user'
16
+ require_relative 'users'
17
+ require_relative 'datasource'
18
+ require_relative 'organization'
19
+ require_relative 'organizations'
20
+ require_relative 'dashboard'
21
+ require_relative 'dashboard_versions'
22
+ require_relative 'snapshot'
23
+
24
+ # -------------------------------------------------------------------------------------------------------------------
25
+ #
26
+ # @abstract # Namespace for classes and modules that handle all Grafana API calls
27
+ #
28
+ # @author Bodo Schulz <bodo@boone-schulz.de>
29
+ #
30
+ #
31
+ module Grafana
32
+
33
+ # Abstract base class for the API calls.
34
+ # Provides some helper methods
35
+ #
36
+ # @author Bodo Schulz
37
+ #
38
+ class Client
39
+
40
+ include Logging
41
+
42
+ include Grafana::Version
43
+ include Grafana::Validator
44
+ include Grafana::Login
45
+ include Grafana::Network
46
+ include Grafana::Tools
47
+ include Grafana::Admin
48
+ include Grafana::Annotations
49
+ include Grafana::User
50
+ include Grafana::Users
51
+ include Grafana::Datasource
52
+ include Grafana::Organization
53
+ include Grafana::Organizations
54
+ include Grafana::Dashboard
55
+ include Grafana::DashboardVersions
56
+ include Grafana::Snapshot
57
+
58
+ attr_accessor :debug
59
+
60
+ # Create a new instance of Class
61
+ #
62
+ # @param [Hash, #read] settings the settings for Grafana
63
+ # @option settings [String] :host ('localhost') the Grafana Hostname
64
+ # @option settings [Integer] :port (3000) the Grafana HTTP Port
65
+ # @option settings [String] :url_path ('')
66
+ # @option settings [Bool] :ssl (false)
67
+ # @option settings [Integer] :timeout (5)
68
+ # @option settings [Integer] :open_timeout (5)
69
+ # @option settings [Hash] :http_headers ({})
70
+ # @option settings [Bool] :debug (false)
71
+ #
72
+ # @example to create an new Instance
73
+ # config = {
74
+ # grafana: {
75
+ # host: '192.168.33.5',
76
+ # port: 3000,
77
+ # url_path: '/grafana',
78
+ # ssl: false,
79
+ # timeout: 10,
80
+ # open_timeout: 10,
81
+ # debug: true
82
+ # }
83
+ #
84
+ # @grafana = Grafana::Client.new(config)
85
+ #
86
+ #
87
+ def initialize( settings )
88
+
89
+ raise ArgumentError.new('only Hash are allowed') unless( settings.is_a?(Hash) )
90
+ raise ArgumentError.new('missing settings') if( settings.size.zero? )
91
+
92
+ host = settings.dig(:grafana, :host) || 'localhost'
93
+ port = settings.dig(:grafana, :port) || 3000
94
+ url_path = settings.dig(:grafana, :url_path) || ''
95
+ ssl = settings.dig(:grafana, :ssl) || false
96
+ @timeout = settings.dig(:grafana, :timeout) || 5
97
+ @open_timeout = settings.dig(:grafana, :open_timeout) || 5
98
+ @http_headers = settings.dig(:grafana, :http_headers) || {}
99
+ @debug = settings.dig(:debug) || false
100
+
101
+ raise ArgumentError.new('missing \'host\'') if( host.nil? )
102
+
103
+ raise ArgumentError.new(format('wrong type. \'port\' must be an Integer, given \'%s\'', port.class.to_s)) unless( port.is_a?(Integer) )
104
+ raise ArgumentError.new(format('wrong type. \'url_path\' must be an String, given \'%s\'', url_path.class.to_s)) unless( url_path.is_a?(String) )
105
+ raise ArgumentError.new(format('wrong type. \'ssl\' must be an Boolean, given \'%s\'', ssl.class.to_s)) unless( ssl.is_a?(Boolean) )
106
+ raise ArgumentError.new(format('wrong type. \'timeout\' must be an Integer, given \'%s\'', @timeout.class.to_s)) unless( @timeout.is_a?(Integer) )
107
+ raise ArgumentError.new(format('wrong type. \'open_timeout\' must be an Integer, given \'%s\'', @open_timeout.class.to_s)) unless( @open_timeout.is_a?(Integer) )
108
+
109
+ protocoll = ssl == true ? 'https' : 'http'
110
+ raise ArgumentError.new(format('wrong \'protocoll\'. only \'http\' or \'https\' allowed, given \'%s\'', protocoll)) if( %w[http https].include?(protocoll.downcase) == false )
111
+
112
+ @url = format( '%s://%s:%d%s', protocoll, host, port, url_path )
113
+ end
114
+
115
+ def self.logger
116
+ @@logger ||= defined?(Logging) ? Logging.logger : Logger.new(STDOUT)
117
+ end
118
+
119
+ def self.logger=(logger)
120
+ @@logger = logger
121
+ end
122
+
123
+ end
124
+
125
+ end
126
+
@@ -0,0 +1,149 @@
1
+
2
+ module Grafana
3
+
4
+ # http://docs.grafana.org/http_api/dashboard/
5
+ #
6
+ module Dashboard
7
+
8
+
9
+ # Get dashboard
10
+ # GET /api/dashboards/db/:slug
11
+ def dashboard( name )
12
+
13
+ raise ArgumentError.new(format('wrong type. \'name\' must be an String, given \'%s\'', name.class.to_s)) unless( name.is_a?(String) )
14
+ raise ArgumentError.new('missing name') if( name.size.zero? )
15
+
16
+ # raise ArgumentError.new('name must be an String') unless( name.is_a?(String) )
17
+
18
+ endpoint = format( '/api/dashboards/db/%s', slug(name) )
19
+
20
+ @logger.debug( "Attempting to get dashboard (GET /api/dashboards/db/#{name})" ) if @debug
21
+
22
+ get( endpoint )
23
+ end
24
+
25
+ # Create / Update dashboard
26
+ # POST /api/dashboards/db
27
+ def create_dashboard( params )
28
+
29
+ raise ArgumentError.new(format('wrong type. params must be an Hash, given %s', params.class.to_s ) ) unless( params.is_a?(Hash) )
30
+
31
+ title = params.dig(:title)
32
+ dashboard = params.dig(:dashboard)
33
+
34
+ # raise ArgumentError.new('missing title') if( title.nil? )
35
+ raise ArgumentError.new('missing dashboard') if( dashboard.nil? )
36
+ raise ArgumentError.new(format('wrong type. dashboard must be an Hash, given %s', dashboard.class.to_s ) ) unless( dashboard.is_a?(Hash) )
37
+
38
+ endpoint = '/api/dashboards/db'
39
+ # title = slug(title)
40
+
41
+ # dashboard = JSON.parse( dashboard ) if( dashboard.is_a?(String) )
42
+ dashboard = regenerate_template_ids( dashboard )
43
+
44
+ if( title.nil? )
45
+ db = JSON.parse( dashboard ) if( dashboard.is_a?(String) )
46
+ title = db.dig('dashboard','title')
47
+ end
48
+
49
+ @logger.debug("Creating dashboard: #{title} (POST /api/dashboards/db)") if @debug
50
+
51
+ post( endpoint, dashboard )
52
+ end
53
+
54
+ # Delete dashboard
55
+ # DELETE /api/dashboards/db/:slug
56
+ def delete_dashboard( name )
57
+
58
+ endpoint = format( '/api/dashboards/db/%s', slug(name) )
59
+
60
+ @logger.debug("Deleting dashboard #{slug(name)} (DELETE #{endpoint})") if @debug
61
+
62
+ delete(endpoint)
63
+ end
64
+
65
+ # Gets the home dashboard
66
+ # GET /api/dashboards/home
67
+ def home_dashboard
68
+
69
+ endpoint = '/api/dashboards/home'
70
+
71
+ @logger.debug("Attempting to get home dashboard (GET #{endpoint})") if @debug
72
+
73
+ get(endpoint)
74
+ end
75
+
76
+ # Tags for Dashboard
77
+ # GET /api/dashboards/tags
78
+ def dashboard_tags
79
+
80
+ endpoint = '/api/dashboards/tags'
81
+
82
+ @logger.debug("Attempting to get dashboard tags(GET #{endpoint})") if @debug
83
+
84
+ get(endpoint)
85
+ end
86
+
87
+ # Search Dashboards
88
+ # GET /api/search/
89
+ #
90
+ # searchDashboards( { :tags => host } )
91
+ # searchDashboards( { :tags => [ host, 'tag1' ] } )
92
+ # searchDashboards( { :tags => [ 'tag2' ] } )
93
+ # searchDashboards( { :query => title } )
94
+ # searchDashboards( { :starred => true } )
95
+ def search_dashboards( params = {} )
96
+
97
+ raise ArgumentError.new(format('wrong type. \'params\' must be an Hash, given \'%s\'', params.class.to_s)) unless( params.is_a?(Hash) )
98
+
99
+ query = params.dig(:query)
100
+ starred = params.dig(:starred)
101
+ tags = params.dig(:tags)
102
+ api = []
103
+
104
+ api << format( 'query=%s', CGI.escape( query ) ) unless query.nil?
105
+ api << format( 'starred=%s', starred ? 'true' : 'false' ) unless( starred.nil? )
106
+
107
+ unless( tags.nil? )
108
+
109
+ tags = tags.join( '&tag=' ) if( tags.is_a?( Array ) )
110
+
111
+ api << format( 'tag=%s', tags )
112
+ end
113
+
114
+ api = api.join( '&' )
115
+
116
+ endpoint = format( '/api/search/?%s' , api )
117
+
118
+ @logger.debug("Attempting to search for dashboards (GET #{endpoint})") if @debug
119
+
120
+ get( endpoint )
121
+ end
122
+
123
+
124
+ def import_dashboards_from_directory( directory )
125
+
126
+ raise ArgumentError.new('directory must be an String') unless( directory.is_a?(String) )
127
+
128
+ result = {}
129
+
130
+ dirs = Dir.glob( format( '%s/**.json', directory ) ).sort
131
+
132
+ dirs.each do |f|
133
+
134
+ @logger.debug( format( 'import \'%s\'', f ) ) if @debug
135
+
136
+ dashboard = File.read( f )
137
+ dashboard = JSON.parse( dashboard )
138
+ title = dashboard.dig('dashboard','title') || f
139
+
140
+ result[f.to_s] ||= {}
141
+ result[f.to_s] = create_dashboard( title: title, dashboard: dashboard )
142
+ end
143
+
144
+ result
145
+ end
146
+
147
+ end
148
+
149
+ end
@@ -0,0 +1,30 @@
1
+
2
+ module Grafana
3
+
4
+ # http://docs.grafana.org/http_api/annotations/
5
+ #
6
+ module DashboardVersions
7
+
8
+ # Get all dashboard versions
9
+ # http://docs.grafana.org/http_api/dashboard_versions/#get-all-dashboard-versions
10
+ # GET /api/dashboards/id/:dashboardId/versions
11
+ def dashboard_all_versions( params ); end
12
+
13
+ # Get dashboard version
14
+ # http://docs.grafana.org/http_api/dashboard_versions/#get-dashboard-version
15
+ # GET /api/dashboards/id/:dashboardId/versions/:id
16
+ def dashboard_version( params ); end
17
+
18
+ # Restore dashboard
19
+ # http://docs.grafana.org/http_api/dashboard_versions/#restore-dashboard
20
+ # POST /api/dashboards/id/:dashboardId/restore
21
+ def restore_dashboard( params ); end
22
+
23
+ # Compare dashboard versions
24
+ # http://docs.grafana.org/http_api/dashboard_versions/#compare-dashboard-versions
25
+ # POST /api/dashboards/calculate-diff
26
+ def compare_dashboard_version( params ); end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,271 @@
1
+
2
+ module Grafana
3
+
4
+ # http://docs.grafana.org/http_api/datasource/
5
+ #
6
+ module Datasource
7
+
8
+ # Get all datasources
9
+ #
10
+ # @example
11
+ # datasources
12
+ #
13
+ # @return [Hash]
14
+ #
15
+ def datasources
16
+
17
+ endpoint = '/api/datasources'
18
+
19
+ @logger.debug("Attempting to get all existing data sources (GET #{endpoint})") if @debug
20
+
21
+ datasources = get( endpoint )
22
+
23
+ if datasources.nil? || datasources.dig('status').to_i != 200
24
+ return {
25
+ 'status' => 404,
26
+ 'message' => 'No Datasources found'
27
+ }
28
+ end
29
+
30
+ datasources = datasources.dig('message')
31
+
32
+ datasource_map = {}
33
+ datasources.each do |ds|
34
+ datasource_map[ds['id']] = ds
35
+ end
36
+
37
+ datasource_map
38
+ end
39
+
40
+ # Get a single datasources by Id or Name
41
+ #
42
+ # @example
43
+ # datasource( 1 )
44
+ # datasource( 'foo' )
45
+ #
46
+ # @return [Hash]
47
+ #
48
+ def datasource( datasource_id )
49
+
50
+ raise ArgumentError.new(format('wrong type. user \'datasource_id\' must be an String (for an Datasource name) or an Integer (for an Datasource Id), given \'%s\'', datasource_id.class.to_s)) if( datasource_id.is_a?(String) && datasource_id.is_a?(Integer) )
51
+ raise ArgumentError.new('missing \'datasource_id\'') if( datasource_id.size.zero? )
52
+
53
+ if(datasource_id.is_a?(String))
54
+ data = datasources.select { |_k,v| v['name'] == datasource_id }
55
+ datasource_id = data.keys.first if( data )
56
+ end
57
+
58
+ if( datasource_id.nil? )
59
+ return {
60
+ 'status' => 404,
61
+ 'message' => format( 'No Datasource \'%s\' found', datasource_id)
62
+ }
63
+ end
64
+
65
+ raise format('DataSource Id can not be 0') if( datasource_id.zero? )
66
+
67
+ endpoint = format('/api/datasources/%d', datasource_id )
68
+
69
+ @logger.debug("Attempting to get existing data source Id #{datasource_id} (GET #{endpoint})") if @debug
70
+
71
+ get(endpoint)
72
+ end
73
+
74
+ # Get a single data source by Name
75
+ # GET /api/datasources/name/:name
76
+
77
+ # Get data source Id by Name
78
+ # GET /api/datasources/id/:name
79
+
80
+ # Update an existing data source
81
+ #
82
+ # merge an current existing datasource configuration with the new values
83
+ #
84
+ # @param [Hash] params
85
+ # @option params [Hash] data
86
+ # @option params [Mixed] datasource Datasource Name (String) or Datasource Id (Integer)
87
+ #
88
+ # @example
89
+ # update_datasource(
90
+ # datasource: 'graphite',
91
+ # data: { url: 'http://localhost:2003' }
92
+ # )
93
+ #
94
+ # @return [Hash]
95
+ #
96
+ def update_datasource( params )
97
+
98
+ raise ArgumentError.new(format('wrong type. \'params\' must be an Hash, given \'%s\'', params.class.to_s)) unless( params.is_a?(Hash) )
99
+ raise ArgumentError.new('missing \'params\'') if( params.size.zero? )
100
+
101
+ data = validate( params, required: true, var: 'data', type: Hash )
102
+ datasource = validate( params, required: true, var: 'datasource' )
103
+
104
+ raise ArgumentError.new(format('wrong type. user \'datasource\' must be an String (for an Datasource name) or an Integer (for an Datasource Id), given \'%s\'', datasource.class.to_s)) if( datasource.is_a?(String) && datasource.is_a?(Integer) )
105
+
106
+ existing_ds = datasource(datasource)
107
+
108
+ existing_ds.reject! { |x| x == 'status' }
109
+ existing_ds = existing_ds.deep_string_keys
110
+
111
+ datasource_id = existing_ds.dig('id')
112
+
113
+ payload = data.deep_string_keys
114
+ payload = existing_ds.merge(payload).deep_symbolize_keys
115
+
116
+ endpoint = format('/api/datasources/%d', datasource_id )
117
+ @logger.debug("Updating data source Id #{datasource_id} (GET #{endpoint})") if @debug
118
+ logger.debug(payload.to_json) if(@debug)
119
+
120
+ put( endpoint, payload.to_json )
121
+ end
122
+
123
+ # Create data source
124
+ #
125
+ # @param [Hash] params
126
+ # @option params [String] type Datasource Type - (required) (grafana graphite cloudwatch elasticsearch prometheus influxdb mysql opentsdb postgres)
127
+ # @option params [String] name Datasource Name - (required)
128
+ # @option params [String] database Datasource Database - (required)
129
+ # @option params [String] access (proxy) Acess Type - (required) (proxy or direct)
130
+ # @option params [Boolean] default (false)
131
+ # @option params [String] user
132
+ # @option params [String] password
133
+ # @option params [String] url Datasource URL - (required)
134
+ # @option params [Hash] json_data
135
+ # @option params [Hash] json_secure
136
+ # @option params [String] basic_user
137
+ # @option params [String] basic_password
138
+ #
139
+ # @example
140
+ # params = {
141
+ # name: 'graphite',
142
+ # type: 'graphite',
143
+ # database: 'graphite',
144
+ # url: 'http://localhost:8080'
145
+ # }
146
+ # create_datasource(params)
147
+ #
148
+ # params = {
149
+ # name: 'graphite',
150
+ # type: 'graphite',
151
+ # database: 'graphite',
152
+ # default: true,
153
+ # url: 'http://localhost:8080',
154
+ # json_data: { graphiteVersion: '1.1' }
155
+ # }
156
+ # create_datasource(params)
157
+ #
158
+ # params = {
159
+ # name: 'test_datasource',
160
+ # type: 'cloudwatch',
161
+ # url: 'http://monitoring.us-west-1.amazonaws.com',
162
+ # json_data: {
163
+ # authType: 'keys',
164
+ # defaultRegion: 'us-west-1'
165
+ # },
166
+ # json_secure: {
167
+ # accessKey: 'Ol4pIDpeKSA6XikgOl4p',
168
+ # secretKey: 'dGVzdCBrZXkgYmxlYXNlIGRvbid0IHN0ZWFs'
169
+ # }
170
+ # }
171
+ # create_datasource(params)
172
+ #
173
+ # @return [Hash]
174
+ #
175
+ def create_datasource( params )
176
+
177
+ raise ArgumentError.new(format('wrong type. \'params\' must be an Hash, given \'%s\'', params.class.to_s)) unless( params.is_a?(Hash) )
178
+ raise ArgumentError.new('missing \'params\'') if( params.size.zero? )
179
+
180
+ type = validate( params, required: true, var: 'type', type: String )
181
+ name = validate( params, required: true, var: 'name', type: String )
182
+ database = validate( params, required: true, var: 'database', type: String )
183
+ access = validate( params, required: false, var: 'access', type: String ) || 'proxy'
184
+ default = validate( params, required: false, var: 'default', type: Boolean ) || false
185
+ user = validate( params, required: false, var: 'user', type: String )
186
+ password = validate( params, required: false, var: 'password', type: String )
187
+ url = validate( params, required: true, var: 'url', type: String )
188
+ json_data = validate( params, required: false, var: 'json_data', type: Hash )
189
+ json_secure = validate( params, required: false, var: 'json_secure', type: Hash )
190
+ ba_user = validate( params, required: false, var: 'basic_user', type: String )
191
+ ba_password = validate( params, required: false, var: 'basic_password', type: String )
192
+
193
+ basic_auth = false
194
+ basic_auth = true unless( ba_user.nil? && ba_password.nil? )
195
+
196
+ valid_types = %w[grafana graphite cloudwatch elasticsearch prometheus influxdb mysql opentsdb postgres]
197
+
198
+ raise ArgumentError.new(format('wrong datasource type. only %s allowed, given \%s\'', valid_types.join(', '), type)) if( valid_types.include?(type.downcase) == false )
199
+
200
+ payload = {
201
+ isDefault: default,
202
+ basicAuth: basic_auth,
203
+ basicAuthUser: ba_user,
204
+ basicAuthPassword: ba_password,
205
+ name: name,
206
+ type: type,
207
+ url: url,
208
+ access: access,
209
+ jsonData: json_data,
210
+ secureJsonData: json_secure
211
+ }
212
+
213
+ payload.reject!{ |_k, v| v.nil? }
214
+
215
+ if( @debug )
216
+ logger.debug("Creating data source: #{name} (database: #{database})")
217
+ logger.debug( payload.to_json )
218
+ end
219
+
220
+ endpoint = '/api/datasources'
221
+ post(endpoint, payload.to_json)
222
+ end
223
+
224
+ # Delete an existing data source by id
225
+ #
226
+ # @param [Mixed] datasource_id Datasource Name (String) or Datasource Id (Integer) for delete Datasource
227
+ #
228
+ # @example
229
+ # delete_datasource( 1 )
230
+ # delete_datasource( 'foo' )
231
+ #
232
+ # @return [Hash]
233
+ #
234
+ def delete_datasource( datasource_id )
235
+
236
+ raise ArgumentError.new(format('wrong type. user \'datasource_id\' must be an String (for an Datasource name) or an Integer (for an Datasource Id), given \'%s\'', datasource_id.class.to_s)) if( datasource_id.is_a?(String) && datasource_id.is_a?(Integer) )
237
+ raise ArgumentError.new('missing \'datasource_id\'') if( datasource_id.size.zero? )
238
+
239
+ if(datasource_id.is_a?(String))
240
+ data = datasources.select { |_k,v| v['name'] == datasource_id }
241
+ datasource_id = data.keys.first if( data )
242
+ end
243
+
244
+ if( datasource_id.nil? )
245
+ return {
246
+ 'status' => 404,
247
+ 'message' => format( 'No Datasource \'%s\' found', datasource_id)
248
+ }
249
+ end
250
+
251
+ raise format('Data Source Id can not be 0') if( datasource_id.zero? )
252
+
253
+ endpoint = format('/api/datasources/%d', datasource_id)
254
+ logger.debug("Deleting data source Id #{datasource_id} (DELETE #{endpoint})") if @debug
255
+
256
+ delete(endpoint)
257
+ end
258
+
259
+
260
+ # Delete an existing data source by name
261
+ # DELETE /api/datasources/name/:datasourceName
262
+
263
+ # Data source proxy calls
264
+ # GET /api/datasources/proxy/:datasourceId/*
265
+
266
+
267
+
268
+ end
269
+
270
+ end
271
+