# The base class for all of our Nagios object types.  Everything else
# is mostly just data.
class Nagios::Base

class UnknownNagiosType < RuntimeError # When an unknown type is asked for by name.
end

include Enumerable

class << self
attr_accessor :parameters, :derivatives, :ocs, :name, :att
attr_accessor :ldapbase

attr_writer :namevar

attr_reader :superior
end

# Attach one class to another.
def self.attach(hash)
@attach ||= {}
hash.each do |n, v| @attach[n] = v end
end

# Convert a parameter to camelcase
def self.camelcase(param)
param.gsub(/_./) do |match|
match.sub(/_/,'').capitalize
end
end

# Uncamelcase a parameter.
def self.decamelcase(param)
param.gsub(/[A-Z]/) do |match|
"_" + match.downcase
end
end

# Create a new instance of a given class.
def self.create(name, args = {})
name = name.intern if name.is_a? String

if @types.include?(name)
@types[name].new(args)
else
raise UnknownNagiosType, "Unknown type %s" % name
end
end

# Yield each type in turn.
def self.eachtype
@types.each do |name, type|
yield [name, type]
end
end

# Create a mapping.
def self.map(hash)
@map ||= {}
hash.each do |n, v| @map[n] = v end
end

# Return a mapping (or nil) for a param
def self.mapping(name)
name = name.intern if name.is_a? String
if defined? @map
@map[name]
else
nil
end
end

# Return the namevar for the canonical name.
def self.namevar
if defined? @namevar
return @namevar
else
if parameter?(:name)
return :name
elsif tmp = (self.name.to_s + "_name").intern and parameter?(tmp)
@namevar = tmp
return @namevar
else
raise "Type %s has no name var" % self.name
end
end
end

# Create a new type.
def self.newtype(name, &block)
name = name.intern if name.is_a? String

@types ||= {}

# Create the class, with the correct name.
t = Class.new(self)
t.name = name

# Everyone gets this. There should probably be a better way, and I
# should probably hack the attribute system to look things up based on
# this "use" setting, but, eh.
t.parameters = [:use]

const_set(name.to_s.capitalize,t)

# Evaluate the passed block. This should usually define all of the work.
t.class_eval(&block)

@types[name] = t
end

# Define both the normal case and camelcase method for a parameter
def self.paramattr(name)
camel = camelcase(name)
param = name

[name, camel].each do |method|
define_method(method) do
@parameters[param]
end

define_method(method.to_s + "=") do |value|
@parameters[param] = value
end
end

end

# Is the specified name a valid parameter?
def self.parameter?(name)
name = name.intern if name.is_a? String
return @parameters.include?(name)
end

# Manually set the namevar
def self.setnamevar(name)
name = name.intern if name.is_a? String
@namevar = name
end

# Set the valid parameters for this class
def self.setparameters(*array)
@parameters += array
end

# Set the superior ldap object class. Seems silly to include this
# in this class, but, eh.
def self.setsuperior(name)
@superior = name
end

# Parameters to suppress in output.
def self.suppress(name)
@suppress ||= []
@suppress << name
end

# Whether a given parameter is suppressed.
def self.suppress?(name)
defined? @suppress and @suppress.include?(name)
end

# Return our name as the string.
def self.to_s
self.name.to_s
end

# Return a type by name.
def self.type(name)
name = name.intern if name.is_a? String

@types[name]
end

# Convenience methods.
def [](param)
send(param)
end

# Convenience methods.
def []=(param,value)
send(param.to_s + "=", value)
end

# Iterate across all ofour set parameters.
def each
@parameters.each { |param,value|
yield(param,value)
}
end

# Initialize our object, optionally with a list of parameters.
def initialize(args = {})
@parameters = {}

args.each { |param,value|
self[param] = value
}
end

# Handle parameters like attributes.
def method_missing(mname, *args)
pname = mname.to_s
pname.sub!(/=/, '')

if self.class.parameter?(pname)
if pname =~ /A-Z/
pname = self.class.decamelcase(pname)
end
self.class.paramattr(pname)

# Now access the parameters directly, to make it at least less
# likely we'll end up in an infinite recursion.
if mname.to_s =~ /=$/
@parameters[pname] = *args
else
return @parameters[mname]
end
else
super
end
end

# Retrieve our name, through a bit of redirection.
def name
send(self.class.namevar)
end

# This is probably a bad idea.
def name=(value)
unless self.class.namevar.to_s == "name"
send(self.class.namevar.to_s + "=", value)
end
end

def namevar
return (self.type + "_name").intern
end

def parammap(param)
unless defined? @map
map = {
self.namevar => "cn"
}
if self.class.map
map.update(self.class.map)
end
end
if map.include?(param)
return map[param]
else
return "nagios-" + param.id2name.gsub(/_/,'-')
end
end

def parent
unless defined? self.class.attached
puts "Duh, you called parent on an unattached class"
return
end

klass,param = self.class.attached
unless @parameters.include?(param)
puts "Huh, no attachment param"
return
end
klass[@parameters[param]]
end

# okay, this sucks
# how do i get my list of ocs?
def to_ldif
base = self.class.ldapbase
str = self.dn + "\n"
ocs = Array.new
if self.class.ocs
# i'm storing an array, so i have to flatten it and stuff
kocs = self.class.ocs
ocs.push(*kocs)
end
ocs.push "top"
oc = self.class.to_s
oc.sub!(/Nagios/,'nagios')
oc.sub!(/::/,'')
ocs.push oc
ocs.each { |oc|
str += "objectclass: " + oc + "\n"
}
@parameters.each { |name,value|
if self.class.suppress.include?(name)
next
end
ldapname = self.parammap(name)
str += ldapname + ": " + value + "\n"
}
str += "\n"
str
end

def to_s
str = "define #{self.type} {\n"

self.each { |param,value|
str += %{\t%-30s %s\n} % [ param,
if value.is_a? Array
value.join(",")
else
value
end
]
}

str += "}\n"

str
end

# The type of object we are.
def type
self.class.name
end

# object types
newtype :command do
setparameters :command_name, :command_line
end

newtype :contact do
setparameters :contact_name, :alias, :host_notification_period,
:host_notification_commands, :service_notification_period,
:service_notification_commands, :register, :email, :pager,
:service_notification_options, :host_notification_options

setsuperior "person"
end

newtype :contactgroup do
setparameters :contactgroup_name, :alias, :members
end

newtype :host do
setparameters :host_name, :notifications_enabled, :event_handler_enabled,
:flap_detection_enabled, :process_perf_data, :retain_status_information,
:retain_nonstatus_information, :register, :use, :alias,
:address, :check_command, :max_check_attempts, :notification_interval,
:notification_period, :notification_options, :checks_enabled,
:failure_prediction_enabled, :parents, :contact_groups, :check_period

setsuperior "person"
map :address => "ipHostNumber"
end

newtype :hostextinfo do
auxiliary = true
setparameters :host_name, :notes_url, :icon_image, :icon_image_alt, :vrml_image,
"2d_coords".intern, "3d_coords".intern

setnamevar :host_name
end

newtype :hostgroup do
setparameters :hostgroup_name, :alias, :contact_groups, :members
end

newtype :hostescalation do
setparameters :name, :first_notification, :last_notification,
:notification_interval, :contact_groups,
:escalation_options, :register, :hostgroup_name
setnamevar :name
end

newtype :hostgroupescalation do
auxiliary = true
setparameters :hostgroup_name, :first_notification, :last_notification,
:contact_groups, :notification_interval

setnamevar :hostgroup_name
end

newtype :service do
attach :host => :host_name
setparameters :name, :active_checks_enabled, :passive_checks_enabled,
:parallelize_check, :obsess_over_service, :check_freshness,
:notifications_enabled, :event_handler_enabled,
:flap_detection_enabled, :process_perf_data,
:retain_status_information, :retain_nonstatus_information, :register,
:is_volatile, :check_period, :max_check_attempts,
:normal_check_interval, :retry_check_interval, :contact_groups,
:notification_interval, :notification_period, :notification_options,
:service_description, :host_name, :freshness_threshold,
:check_command, :hostgroup_name, :event_handler, :servicegroups, :host

suppress :host_name

setnamevar :service_description
end

newtype :servicedependency do
auxiliary = true
setparameters :host_name, :service_description, :dependent_host_name,
:dependent_service_description, :execution_failure_criteria,
:notification_failure_criteria, :hostgroup_name,
:dependent_hostgroup_name

setnamevar :host_name
end

newtype :serviceescalation do
setparameters :host_name, :service_description, :first_notification,
:last_notification, :contact_groups, :notification_interval, :hostgroup_name

setnamevar :host_name
end

newtype :servicegroup do
setparameters :servicegroup_name, :alias

setnamevar :servicegroup_name
end

newtype :serviceextinfo do
auxiliary = true

setparameters :host_name, :service_description, :icon_image, :icon_image_alt

setnamevar :host_name
end

newtype :timeperiod do
setparameters :timeperiod_name, :alias, :sunday, :monday, :tuesday,
:wednesday, :thursday, :friday, :saturday
end
end

# $Id$