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

Added screen resource with provider, supporting the usual actions #202

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
9 changes: 9 additions & 0 deletions .kitchen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ suites:
run_list:
- "recipe[zabbix::default]"
attributes:
- name: screen_resource
run_list:
- recipe[apt]
- recipe[zabbix_test::screen_resource]
attributes:
excludes:
- ubuntu-10.04
- centos-5.9
- centos-6.4
5 changes: 5 additions & 0 deletions Berksfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
source 'https://api.berkshelf.com'

metadata

cookbook 'apt'
cookbook 'mysql', '~> 5.5.4'

cookbook 'zabbix_test', :path => './test/kitchen/cookbooks/zabbix_test'
208 changes: 208 additions & 0 deletions providers/screen.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
action :create_or_update do
Chef::Zabbix.with_connection(new_resource.server_connection) do |connection|
get_screen_request = {
:method => 'screen.get',
:params => {
:filter => {
:name => new_resource.name
}
}
}
screens = connection.query(get_screen_request)

if screens.size == 0
Chef::Log.info "Screen does not exists. Proceeding to create this screen on the Zabbix server: '#{new_resource.name}'"
run_action :create
new_resource.updated_by_last_action(true)
else
Chef::Log.info "Going to update this screen (if needed): '#{new_resource.name}'"
run_action :update
end
end
end

action :create do
Chef::Zabbix.with_connection(new_resource.server_connection) do |connection|

looked_up_screen_items = add_resource_ids_for_screen_items(new_resource.screen_items, connection)

request = {
:method => 'screen.create',
:params => {
:name => new_resource.name,
:hsize => new_resource.hsize,
:vsize => new_resource.vsize,
:screenitems => looked_up_screen_items
}
}
Chef::Log.info "Creating new screen: '#{new_resource.name}'"
connection.query(request)
end
new_resource.updated_by_last_action(true)
end

action :update do
Chef::Zabbix.with_connection(new_resource.server_connection) do |connection|

get_screen_request = {
:method => 'screen.get',
:params => {
:filter => {
:name => new_resource.name
},
:output => :extend,
:selectScreenItems => :extend
}
}
screen = connection.query(get_screen_request)
if screen.nil? || screen.empty?
Chef::Application.fatal! "Could not find screen for update: '#{new_resource.name}'"
else
screen = screen.first
end

looked_up_screen_items = add_resource_ids_for_screen_items(new_resource.screen_items, connection)

need_to_update = false

%w(name vsize hsize).each do |attr_name|
need_to_update = true if screen[attr_name] != new_resource.send(attr_name).to_s
end

# We sort both of the arrays to make sure we can compare their elements
# one by one
screen['screenitems'].sort_by! { |hsh| hsh['resourceid'] }
looked_up_screen_items.sort_by! { |hsh| hsh[:resourceid] }

looked_up_screen_items.each_with_index do |screen_item, index|
screen_item.each do |key, value|
need_to_update = true if screen['screenitems'][index].nil? || value.to_s != screen['screenitems'][index][key.to_s]
end
end

if need_to_update
screen_update_request = {
:method => 'screen.update',
:params => {
:screenid => screen['screenid'],
:name => new_resource.name,
:hsize => new_resource.hsize,
:vsize => new_resource.vsize,
:screenitems => looked_up_screen_items
}
}
Chef::Log.info "Updating screen '#{new_resource.name}'"
connection.query(screen_update_request)
new_resource.updated_by_last_action(true)
else
Chef::Log.info "The attributes of screen '#{new_resource.name}' are already up-to-date, doing nothing"
end

end
end

action :delete do
Chef::Zabbix.with_connection(new_resource.server_connection) do |connection|

get_screen_request = {
:method => 'screen.get',
:params => {
:filter => {
:name => new_resource.name
},
:output => :shorten
}
}
screen = connection.query(get_screen_request)
if screen.nil? || screen.empty?
Chef::Application.fatal! "Could not find screen for deletion: '#{new_resource.name}'"
else
screen = screen.first
end

screen_delete_request = {
:method => 'screen.delete',
:params => [
screen['screenid']
]
}
Chef::Log.info "Deleting screen '#{new_resource.name}'"
result = connection.query(screen_delete_request)
Application.fatal! "Error deleting screen '#{new_resource.name}', see Chef errors" if result.nil? || result.empty? || result['screenids'].nil? || result['screenids'].empty? || !result['screenids'].include?(screen['screenid'])
new_resource.updated_by_last_action(true)
end
end

def load_current_resource
run_context.include_recipe 'zabbix::_providers_common'
require 'zabbixapi'
end

def add_resource_ids_for_screen_items(screen_items, connection)
returned_screen_items = []
screen_items.each do |current_screen_item|
if current_screen_item[:resourceid].is_a? Fixnum
# We make it to a String for possible later comparison/sorting, because the API
# can only return String values anyway
current_screen_item[:resourceid] = current_screen_item[:resourceid].to_s
returned_screen_items.push current_screen_item
else
case current_screen_item[:resourcetype]
when 0
Chef::Application.fatal! "When 'resourcetype' is 0 and 'resourceid' is a Hash, then you must set 'name' in that Hash" if current_screen_item[:resourceid][:name].nil?
Chef::Log.debug "Checking graph id for: host: '#{current_screen_item[:resourceid][:host]}', name: '#{current_screen_item[:resourceid][:name]}'"
current_screen_item[:resourceid] = get_graph_id_from_graph_name(current_screen_item[:resourceid][:host], current_screen_item[:resourceid][:name], connection)
when 1, 3
Chef::Application.fatal! "When 'resourcetype' is 1 or 3 and 'resourceid' is a Hash, then you must set 'key_' in that Hash" if current_screen_item[:resourceid][:key_].nil?
Chef::Log.debug "Checking item id for: host: '#{current_screen_item[:resourceid][:host]}', key: '#{current_screen_item[:resourceid][:key_]}'"
current_screen_item[:resourceid] = get_item_id_from_item_key(current_screen_item[:resourceid][:host], current_screen_item[:resourceid][:key_], connection)
else
Chef::Application.fatal! "You either have to supply a correct Numeric 'resourceid' paramater or if you use a lookup Hash instead, then the 'resourcetype' must be 0, 1 or 3!"
end
returned_screen_items.push current_screen_item
end
end
returned_screen_items
end

def get_graph_id_from_graph_name(host_name, name, connection)
return_id = nil
get_graph_id_request = {
:method => 'host.get',
:params => {
:selectGraphs => ['graphid', 'name'],
:output => :shorten,
:filter => {
:host => host_name
}
}
}
graph_id_result = connection.query(get_graph_id_request)
Chef::Application.fatal! "Could not find graphs at all for host: '#{host_name}'" if graph_id_result.nil? || graph_id_result.empty?
graph_id_result.first['graphs'].each do |graph|
return_id = graph['graphid'] if graph['name'] == name
end
Chef::Application.fatal! "Could not find graph id for host: '#{host_name}', name: '#{name}'" if return_id.nil?
return_id
end

def get_item_id_from_item_key(host_name, key_, connection)
return_id = nil
get_item_id_request = {
:method => 'host.get',
:params => {
:selectItems => ['itemid', 'key_'],
:output => :shorten,
:filter => {
:host => host_name
}
}
}
item_id_result = connection.query(get_item_id_request)
Chef::Application.fatal! "Could not find items at all for host: '#{host_name}'" if item_id_result.nil? || item_id_result.empty?
item_id_result.first['items'].each do |item|
return_id = item['itemid'] if item['key_'] == key_
end
Chef::Application.fatal! "Could not find item id for host: '#{host_name}', key_: '#{key_}'" if return_id.nil?
return_id
end
62 changes: 62 additions & 0 deletions resources/screen.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
actions :create_or_update, :create, :update, :delete
default_action :create_or_update

attribute :name, :kind_of => String, :required => true

attribute :hsize, :kind_of => Integer, :default => 1
attribute :vsize, :kind_of => Integer, :default => 1

# This accepts an Array of Zabbix ScreenItem objects supplied as Ruby
# Hashes for the attributes of attached ScreenItem objects
#
# The `resourceid` is a special attribute, which can either be:
# - Fixnum: in that case that will be used in the Zabbix API as-is
# - Hash: containing the `:host` symbol as a key to indicate which
# host machine's resources will we use, and also containing either of these:
# - name: only valid when the `resourcetype` is 0, it will be used to get
# the graph of the given host as a normal graph resource
# - key_: only valid when the `resourcetype` is 1, it will identify the item
# of the given host to use as a simple graph resource
# Please note: only `resourcetype` 0, 1 and 3 lookups are supported currently
#
# Example:
# [{
# resourcetype: 1,
# colspan: 1,
# rowspan: 1,
# elements: 25,
# height: 200,
# resourceid: {host: 'Host Name', key_: 'system.cpu.load[percpu,avg1]'},
# width: 320,
# x: 1,
# y: 1
# },
# {
# resourcetype: 0,
# colspan: 1,
# rowspan: 1,
# elements: 25,
# height: 200,
# resourceid: {host: 'Host Name', name: 'CPU load'},
# width: 320,
# x: 1,
# y: 1
# },
# {
# resourcetype: 0
# colspan: 1,
# rowspan: 1,
# elements: 25,
# height: 200,
# resourceid: 581,
# width: 320,
# x: 1,
# y: 1
# }]
attribute :screen_items, :kind_of => Array, :default => []

# This is a Ruby Hash object with 3 required parameters used for connecting to
# the Zabbix server's API. An example could be this:
# { url: "https://zabbix.server.address.com/api_jsonrpc.php", user: 'Admin',
# password: 'zabbix' }
attribute :server_connection, :kind_of => Hash, :default => {}
1 change: 1 addition & 0 deletions test/kitchen/Kitchenfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
cookbook "zabbix" do
configuration "default"
configuration "server_source"
configuration "screen_resource"
exclude :platform => 'centos'
exclude :platform => 'debian'
exclude :platform => 'redhat'
Expand Down
3 changes: 3 additions & 0 deletions test/kitchen/cookbooks/zabbix_test/metadata.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name 'zabbix_test'
maintainer 'Efactures'
maintainer_email '[email protected]'
license 'Apache 2.0'
description 'This cookbook is used with test-kitchen to test the parent, zabbix cookbook.'
version '1.0.0'

depends 'zabbix'
76 changes: 76 additions & 0 deletions test/kitchen/cookbooks/zabbix_test/recipes/screen_resource.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Author:: Pal David Gergely (<[email protected]>)
# Cookbook Name:: zabbix-test
# Recipe:: screen_resource
#
# Copyright 2015, Pal David Gergely
#
# Apache 2.0

# First a working Zabbix server is needed to have an API to test the resource
# against it, so intalling it with a database
node.normal['zabbix']['server']['install'] = true
node.normal['zabbix']['server']['version'] = '2.2.8'
node.normal['zabbix']['database']['dbpassword'] = 'foobar'
include_recipe 'mysql::server'
# Needed for the mysql gem install
package 'libmysqlclient-dev'
include_recipe 'zabbix::database'
include_recipe 'zabbix::server'
include_recipe 'zabbix::web'

# Now trigger an immediate restart on Apache to get the Zabbix API working
# before using it, TODO: not very nice, but needed
service 'apache2' do
action :reload
end

# Also add the VM itself to Zabbix hosts, since monitored items are needed for
# the screen items to be able to test them
zabbix_host 'localhost' do
parameters(
groupNames: ['Linux servers'],
templates: ['Template OS Linux'],
interfaces: [Chef::Zabbix::API::HostInterface.new(type: Chef::Zabbix::API::HostInterfaceType.new(1), main: 1, useip: 1, ip: '127.0.0.1', port: 10_050)]
)
server_connection(
url: 'http://127.0.0.1/api_jsonrpc.php',
user: 'Admin',
password: 'zabbix'
)
end

# Then create a simple test screen to validate that the screen resource is
# working
zabbix_screen 'test-screen' do
hsize 2
vsize 2
screen_items [
{
resourcetype: 1,
colspan: 1,
rowspan: 1,
elements: 25,
height: 200,
resourceid: { host: 'localhost', key_: 'system.cpu.load[percpu,avg1]' },
width: 320,
x: 0,
y: 0
},
{
resourcetype: 0,
colspan: 0,
rowspan: 0,
elements: 25,
height: 200,
resourceid: { host: 'localhost', name: 'CPU load' },
width: 320,
x: 1,
y: 1
}
]
server_connection(
url: 'http://127.0.0.1/api_jsonrpc.php',
user: 'Admin',
password: 'zabbix'
)
end