Skip to content

Commit

Permalink
Add grafana_datasource resource
Browse files Browse the repository at this point in the history
This resource is used to configure datasources in Grafana. For now, it
supports InfluxDB 0.9, Graphite, Elasticsearch, Prometheus, KairosDB and
OpenTSDB.
  • Loading branch information
Simon Pasquier committed Nov 12, 2015
1 parent 89fe873 commit 8490960
Show file tree
Hide file tree
Showing 4 changed files with 399 additions and 0 deletions.
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

184 changes: 184 additions & 0 deletions lib/puppet/provider/grafana_datasource/grafana.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# 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)
rescue JSON::ParserError
fail("Fail to parse response: %s" % response.body)
end

datasources.collect do |datasource|
{
: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
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
91 changes: 91 additions & 0 deletions lib/puppet/type/grafana_datasource.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# 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.
#
Puppet::Type.newtype(:grafana_datasource) do
@doc = "Manage datasources in Grafana"

ensurable

newparam(:name, :namevar => true) do
desc "The name of the datasource."
end

newparam(:grafana_url) do
desc "The URL of the Grafana server"
defaultto ""

validate do |value|
unless value =~ /^https?:\/\//
raise ArgumentError , "'%s' is not a valid URL" % value
end
end
end

newparam(:grafana_user) do
desc "The username for the Grafana server"
end

newparam(:grafana_password) do
desc "The password for the Grafana server"
end

newproperty(:url) do
desc "The URL of the datasource"

validate do |value|
unless value =~ /^https?:\/\//
raise ArgumentError , "'%s' is not a valid URL" % value
end
end
end

newproperty(:type) do
desc "The datasource type"
newvalues(:influxdb, :elasticsearch, :graphite, :kairosdb, :opentsdb, :prometheus)
end

newproperty(:user) do
desc "The username for the datasource (optional)"
end

newproperty(:password) do
desc "The password for the datasource (optional)"
end

newproperty(:database) do
desc "The name of the database (optional)"
end

newproperty(:access_mode) do
desc "Whether the datasource is accessed directly or not by the clients"
newvalues(:direct, :proxy)
defaultto :direct
end

newproperty(:is_default) do
desc "Whether the datasource is the default one"
newvalues(:true, :false)
defaultto :false
end

newproperty(:json_data) do
desc "Additional JSON data to configure the datasource (optional)"

validate do |value|
unless value.nil? or value.is_a?(Hash) then
raise ArgumentError , "json_data should be a Hash!"
end
end
end
end
Loading

0 comments on commit 8490960

Please sign in to comment.