Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add grafana_datasource and grafana_dashboard resources #63

Merged
merged 2 commits into from
Mar 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions lib/puppet/provider/grafana.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Copyright 2015 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
require 'cgi'
require 'json'
require 'net/http'

class Puppet::Provider::Grafana < Puppet::Provider
# Helper methods
def grafana_host
unless @grafana_host
@grafana_host = URI.parse(resource[:grafana_url]).host
end
@grafana_host
end

def grafana_port
unless @grafana_port
@grafana_port = URI.parse(resource[:grafana_url]).port
end
@grafana_port
end

def grafana_scheme
unless @grafana_scheme
@grafana_scheme = URI.parse(resource[:grafana_url]).scheme
end
@grafana_scheme
end

# Return a Net::HTTP::Response object
def send_request(operation="GET", path="", data=nil, search_path={})
request = nil
encoded_search = ""

if URI.respond_to?(:encode_www_form)
encoded_search = URI.encode_www_form(search_path)
else
# Ideally we would have use URI.encode_www_form but it isn't
# available with Ruby 1.8.x that ships with CentOS 6.5.
encoded_search = search_path.to_a.map do |x|
x.map{|y| CGI.escape(y.to_s)}.join('=')
end
encoded_search = encoded_search.join('&')
end
uri = URI.parse("%s://%s:%d%s?%s" % [
self.grafana_scheme, self.grafana_host, self.grafana_port,
path, encoded_search])

case operation.upcase
when 'POST'
request = Net::HTTP::Post.new(uri.request_uri)
request.body = data.to_json()
when 'PUT'
request = Net::HTTP::Put.new(uri.request_uri)
request.body = data.to_json()
when 'GET'
request = Net::HTTP::Get.new(uri.request_uri)
when 'DELETE'
request = Net::HTTP::Delete.new(uri.request_uri)
else
raise Puppet::Error, "Unsupported HTTP operation '%s'" % operation
end

request.content_type = 'application/json'
if resource[:grafana_user] and resource[:grafana_user]
request.basic_auth resource[:grafana_user], resource[:grafana_password]
end

return Net::HTTP.start(self.grafana_host, self.grafana_port) do |http|
http.request(request)
end
end
end

109 changes: 109 additions & 0 deletions lib/puppet/provider/grafana_dashboard/grafana.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Copyright 2015 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
require 'json'

require File.expand_path(File.join(File.dirname(__FILE__), '..', 'grafana'))

# Note: this class doesn't implement the self.instances and self.prefetch
# methods because the Grafana API doesn't allow to retrieve the dashboards and
# all their properties in a single call.
Puppet::Type.type(:grafana_dashboard).provide(:grafana, :parent => Puppet::Provider::Grafana) do
desc "Support for Grafana dashboards stored into Grafana"

defaultfor :kernel => 'Linux'

# Return the list of dashboards
def dashboards
response = self.send_request('GET', '/api/search', nil, {:q => '', :starred => false})
if response.code != '200'
fail("Fail to retrieve the dashboards (HTTP response: %s/%s)" %
[response.code, response.body])
end

begin
JSON.parse(response.body)
rescue JSON::ParserError
fail("Fail to parse dashboards (HTTP response: %s/%s)" %
[response_code, response.body])
end
end

# Return the dashboard matching with the resource's title
def find_dashboard
if not self.dashboards.find{ |x| x['title'] == resource[:title] }
return
end

response = self.send_request('GET', '/api/dashboards/db/%s' % self.slug)
if response.code != '200'
fail("Fail to retrieve dashboard '%s' (HTTP response: %s/%s)" %
[resource[:title], response.code, response.body])
end

begin
# Cache the dashboard's content
@dashboard = JSON.parse(response.body)["dashboard"]
rescue JSON::ParserError
fail("Fail to parse dashboard '%s': %s" %
[resource[:title], response.body])
end
end

def save_dashboard(dashboard)
data = {
:dashboard => dashboard.merge({
'title' => resource[:title],
'id' => @dashboard ? @dashboard['id'] : nil,
'version' => @dashboard ? @dashboard['version'] + 1 : 0
}),
:overwrite => @dashboard != nil
}

response = self.send_request('POST', '/api/dashboards/db', data)
if response.code != '200'
fail("Fail to save dashboard '%s' (HTTP response: %s/%s)" %
[resource[:name], response.code, response.body])
end
end

def slug
resource[:title].downcase.gsub(/[^\w\- ]/, '').gsub(/ +/, '-')
end

def content
@dashboard
end

def content=(value)
self.save_dashboard(value)
end

def create
self.save_dashboard(resource[:content])
end

def destroy
response = self.send_request('DELETE', '/api/dashboards/db/%s' % self.slug)

if response.code != '200'
raise Puppet::Error, "Failed to delete dashboard '%s' (HTTP "\
"response: %s/'%s')" % [resource[:title], response.code,
response.body]
end
end

def exists?
self.find_dashboard()
end
end
192 changes: 192 additions & 0 deletions lib/puppet/provider/grafana_datasource/grafana.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# Copyright 2015 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
require 'json'

require File.expand_path(File.join(File.dirname(__FILE__), '..', 'grafana'))

Puppet::Type.type(:grafana_datasource).provide(:grafana, :parent => Puppet::Provider::Grafana) do
desc "Support for Grafana datasources"

defaultfor :kernel => 'Linux'

def datasources
response = self.send_request('GET', '/api/datasources')
if response.code != '200'
fail("Fail to retrieve datasources (HTTP response: %s/%s)" %
[response.code, response.body])
end

begin
datasources = JSON.parse(response.body)

datasources.collect{|x| x["id"]}.collect do |id|
response = self.send_request('GET', '/api/datasources/%s' % id)
if response.code != '200'
fail("Fail to retrieve datasource %d (HTTP response: %s/%s)" %
[id, response.code, response.body])
end

datasource = JSON.parse(response.body)

{
:id => datasource["id"],
:name => datasource["name"],
:url => datasource["url"],
:type => datasource["type"],
:user => datasource["user"],
:password => datasource["password"],
:database => datasource["database"],
:access_mode => datasource["access"],
:is_default => datasource["isDefault"] ? :true : :false,
:json_data => datasource["jsonData"]
}
end
rescue JSON::ParserError
fail("Fail to parse response: %s" % response.body)
end
end

def datasource
unless @datasource
@datasource = self.datasources.find { |x| x[:name] == resource[:name] }
end
@datasource
end

def datasource=(value)
@datasource = value
end

def type
self.datasource[:type]
end

def type=(value)
resource[:type] = value
self.save_datasource()
end

def url
self.datasource[:url]
end

def url=(value)
resource[:url] = value
self.save_datasource()
end

def access_mode
self.datasource[:access_mode]
end

def access_mode=(value)
self.resource[:access_mode] = value
self.save_datasource()
end

def database
self.datasource[:database]
end

def database=(value)
resource[:database] = value
self.save_datasource()
end

def user
self.datasource[:user]
end

def user=(value)
resource[:user] = value
self.save_datasource()
end

def password
self.datasource[:password]
end

def password=(value)
resource[:password] = value
self.save_datasource()
end

def is_default
self.datasource[:is_default]
end

def is_default=(value)
resource[:is_default] = value
self.save_datasource()
end

def json_data
self.datasource[:json_data]
end

def json_data=(value)
resource[:json_data] = value
self.save_datasource()
end

def save_datasource
data = {
:name => resource[:name],
:type => resource[:type],
:url => resource[:url],
:access => resource[:access_mode],
:database => resource[:database],
:user => resource[:user],
:password => resource[:password],
:isDefault => (resource[:is_default] == :true),
:jsonData => resource[:json_data],
}

if self.datasource.nil?
response = self.send_request('POST', '/api/datasources', data)
else
data[:id] = self.datasource[:id]
response = self.send_request('PUT', '/api/datasources/%s' % self.datasource[:id], data)
end

if response.code != '200'
fail("Failed to create save '%s' (HTTP response: %s/'%s')" %
[resource[:name], response.code, response.body])
end
self.datasource = nil
end

def delete_datasource
response = self.send_request('DELETE', '/api/datasources/%s' % self.datasource[:id])

if response.code != '200'
fail("Failed to delete datasource '%s' (HTTP response: %s/'%s')" %
[resource[:name], response.code, response.body])
end
self.datasource = nil
end

def create
self.save_datasource()
end

def destroy
self.delete_datasource()
end

def exists?
self.datasource
end
end
Loading