grafana 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+