naginata 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,64 @@
1
+ require 'naginata'
2
+ require 'naginata/command/external_command'
3
+ require 'naginata/configuration'
4
+ require 'naginata/runner'
5
+
6
+ module Naginata
7
+ class CLI::Notification
8
+
9
+ def initialize(options)
10
+ @options = options
11
+ end
12
+
13
+ def run
14
+ if !@options[:enable] and !@options[:disable]
15
+ abort "Either --enable or --disable options is required"
16
+ end
17
+ if @options[:enable] and @options[:disable]
18
+ abort "Both --enable and --disable options can not be given"
19
+ end
20
+
21
+ if @options[:all_hosts]
22
+ ::Naginata::Configuration.env.add_filter(:host, :all)
23
+ elsif @options[:patterns].empty?
24
+ abort "At least one hostpattern must be given or use --all-hosts option"
25
+ else
26
+ ::Naginata::Configuration.env.add_filter(:host, @options[:patterns])
27
+ end
28
+ if @options[:services]
29
+ ::Naginata::Configuration.env.add_filter(:service, @options[:services])
30
+ end
31
+
32
+ command_file = ::Naginata::Configuration.env.fetch(:nagios_server_options)[:command_file]
33
+
34
+ if !@options[:force]
35
+ Naginata.ui.info "Following notifications will be #{@options[:enable] ? 'enabled' : 'disabled'}", true
36
+ Naginata::Runner.run_locally do |nagios_server, services|
37
+ services.group_by{ |s| s.hostname }.each do |hostname, svcs|
38
+ puts hostname
39
+ svcs.each do |service|
40
+ Naginata.ui.info " - #{service.description}", true
41
+ end
42
+ end
43
+ end
44
+ abort unless Naginata.ui.yes?("Are you sure? [y|N]")
45
+ end
46
+
47
+ Naginata::Runner.run do |backend, nagios_server, services|
48
+ path = nagios_server.fetch(:command_file) || command_file
49
+
50
+ services.each do |service|
51
+ opts = {path: (nagios_server.fetch(:command_file) || command_file), host_name: service.hostname}
52
+ opts.merge!(service_description: service.description) if service.description != :ping
53
+ action = @options[:enable] ? 'enable' : 'disable'
54
+ host_or_svc = service.description == :ping ? 'host' : 'svc'
55
+ command_arg = Naginata::Command::ExternalCommand.send("#{action}_#{host_or_svc}_notifications".to_sym, opts).split(/\s+/, 2)
56
+ command = command_arg.shift.to_sym
57
+ backend.execute command, command_arg
58
+ end
59
+ end
60
+ Naginata.ui.info "Done", true
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,198 @@
1
+ module Naginata::Command
2
+
3
+ class ExternalCommand
4
+
5
+ def self.method_missing(name, *args, &block)
6
+ raise ArgumentError, "only one argment allowed" if args.size > 1
7
+ options = args.first || {}
8
+ dispatch(name, options)
9
+ end
10
+
11
+ def self.dispatch(action, options = {})
12
+ action = action.to_s.upcase.to_sym
13
+ raise ArgumentError, "action name #{action} is not implemented" unless List.include? action
14
+ raise ArgumentError, ":path option is required" if options[:path].nil?
15
+
16
+ ts = options[:ts] || Time.now.to_i
17
+ format = 'echo "[%d] %s" > %s'
18
+ str = [action.to_s, List.keys_for(action).map{|k| options[k.to_sym]}].flatten.join(';')
19
+ format % [ts, str, options[:path]]
20
+ end
21
+
22
+ # ref Original code here
23
+ # https://github.com/ripienaar/ruby-nagios/blob/master/lib/nagios/external_commands/list.rb
24
+ #
25
+ # List of all available nagios external commands, formats and
26
+ # descriptions can be obtained from
27
+ # http://www.nagios.org/developerinfo/externalcommands
28
+ class List
29
+ ACTIONS = {
30
+ :ACKNOWLEDGE_HOST_PROBLEM => %w{host_name sticky notify persistent author comment},
31
+ :ACKNOWLEDGE_SVC_PROBLEM => %w{host_name service_description sticky notify persistent author comment},
32
+ :ADD_HOST_COMMENT => %w{host_name persistent author comment},
33
+ :ADD_SVC_COMMENT => %w{host_name service_description persistent author comment},
34
+ :CHANGE_CONTACT_HOST_NOTIFICATION_TIMEPERIOD => %w{contact_name notification_timeperiod},
35
+ :CHANGE_CONTACT_MODATTR => %w{contact_name value},
36
+ :CHANGE_CONTACT_MODHATTR => %w{contact_name value},
37
+ :CHANGE_CONTACT_MODSATTR => %w{contact_name value},
38
+ :CHANGE_CONTACT_SVC_NOTIFICATION_TIMEPERIOD => %w{contact_name notification_timeperiod},
39
+ :CHANGE_CUSTOM_CONTACT_VAR => %w{contact_name varname varvalue},
40
+ :CHANGE_CUSTOM_HOST_VAR => %w{host_name varname varvalue},
41
+ :CHANGE_CUSTOM_SVC_VAR => %w{host_name service_description varname varvalue},
42
+ :CHANGE_GLOBAL_HOST_EVENT_HANDLER => %w{event_handler_command},
43
+ :CHANGE_GLOBAL_SVC_EVENT_HANDLER => %w{event_handler_command},
44
+ :CHANGE_HOST_CHECK_COMMAND => %w{host_name check_command},
45
+ :CHANGE_HOST_CHECK_TIMEPERIOD => %w{host_name check_timeperod},
46
+ :CHANGE_HOST_EVENT_HANDLER => %w{host_name event_handler_command},
47
+ :CHANGE_HOST_MODATTR => %w{host_name value},
48
+ :CHANGE_MAX_HOST_CHECK_ATTEMPTS => %w{host_name check_attempts},
49
+ :CHANGE_MAX_SVC_CHECK_ATTEMPTS => %w{host_name service_description check_attempts},
50
+ :CHANGE_NORMAL_HOST_CHECK_INTERVAL => %w{host_name check_interval},
51
+ :CHANGE_NORMAL_SVC_CHECK_INTERVAL => %w{host_name service_description check_interval},
52
+ :CHANGE_RETRY_HOST_CHECK_INTERVAL => %w{host_name service_description check_interval},
53
+ :CHANGE_RETRY_SVC_CHECK_INTERVAL => %w{host_name service_description check_interval},
54
+ :CHANGE_SVC_CHECK_COMMAND => %w{host_name service_description check_command},
55
+ :CHANGE_SVC_CHECK_TIMEPERIOD => %w{host_name service_description check_timeperiod},
56
+ :CHANGE_SVC_EVENT_HANDLER => %w{host_name service_description event_handler_command},
57
+ :CHANGE_SVC_MODATTR => %w{host_name service_description value},
58
+ :CHANGE_SVC_NOTIFICATION_TIMEPERIOD => %w{host_name service_description notification_timeperiod},
59
+ :DELAY_HOST_NOTIFICATION => %w{host_name notification_time},
60
+ :DELAY_SVC_NOTIFICATION => %w{host_name service_description notification_time},
61
+ :DEL_ALL_HOST_COMMENTS => %w{host_name},
62
+ :DEL_ALL_SVC_COMMENTS => %w{host_name service_description},
63
+ :DEL_HOST_COMMENT => %w{comment_id},
64
+ :DEL_HOST_DOWNTIME => %w{downtime_id},
65
+ :DEL_SVC_COMMENT => %w{comment_id},
66
+ :DEL_SVC_DOWNTIME => %w{downtime_id},
67
+ :DISABLE_ALL_NOTIFICATIONS_BEYOND_HOST => %w{host_name},
68
+ :DISABLE_CONTACTGROUP_HOST_NOTIFICATIONS => %w{contactgroup_name},
69
+ :DISABLE_CONTACTGROUP_SVC_NOTIFICATIONS => %w{contactgroup_name},
70
+ :DISABLE_CONTACT_HOST_NOTIFICATIONS => %w{contact_name},
71
+ :DISABLE_CONTACT_SVC_NOTIFICATIONS => %w{contact_name},
72
+ :DISABLE_EVENT_HANDLERS => [],
73
+ :DISABLE_FAILURE_PREDICTION => [],
74
+ :DISABLE_FLAP_DETECTION => [],
75
+ :DISABLE_HOSTGROUP_HOST_CHECKS => %w{hostgroup_name},
76
+ :DISABLE_HOSTGROUP_HOST_NOTIFICATIONS => %w{hostgroup_name},
77
+ :DISABLE_HOSTGROUP_PASSIVE_HOST_CHECKS => %w{hostgroup_name},
78
+ :DISABLE_HOSTGROUP_PASSIVE_SVC_CHECKS => %w{hostgroup_name},
79
+ :DISABLE_HOSTGROUP_SVC_CHECKS => %w{hostgroup_name},
80
+ :DISABLE_HOSTGROUP_SVC_NOTIFICATIONS => %w{hostgroup_name},
81
+ :DISABLE_HOST_AND_CHILD_NOTIFICATIONS => %w{host_name},
82
+ :DISABLE_HOST_CHECK => %w{host_name},
83
+ :DISABLE_HOST_EVENT_HANDLER => %w{host_name},
84
+ :DISABLE_HOST_FLAP_DETECTION => %w{host_name},
85
+ :DISABLE_HOST_FRESHNESS_CHECKS => [],
86
+ :DISABLE_HOST_NOTIFICATIONS => %w{host_name},
87
+ :DISABLE_HOST_SVC_CHECKS => %w{host_name},
88
+ :DISABLE_HOST_SVC_NOTIFICATIONS => %w{host_name},
89
+ :DISABLE_NOTIFICATIONS => [],
90
+ :DISABLE_PASSIVE_HOST_CHECKS => %w{host_name},
91
+ :DISABLE_PASSIVE_SVC_CHECKS => %w{host_name service_description},
92
+ :DISABLE_PERFORMANCE_DATA => [],
93
+ :DISABLE_SERVICEGROUP_HOST_CHECKS => %w{servicegroup_name},
94
+ :DISABLE_SERVICEGROUP_HOST_NOTIFICATIONS => %w{servicegroup_name},
95
+ :DISABLE_SERVICEGROUP_PASSIVE_HOST_CHECKS => %w{servicegroup_name},
96
+ :DISABLE_SERVICEGROUP_PASSIVE_SVC_CHECKS => %w{servicegroup_name},
97
+ :DISABLE_SERVICEGROUP_SVC_CHECKS => %w{servicegroup_name},
98
+ :DISABLE_SERVICEGROUP_SVC_NOTIFICATIONS => %w{servicegroup_name},
99
+ :DISABLE_SERVICE_FLAP_DETECTION => %w{host_name service_description},
100
+ :DISABLE_SERVICE_FRESHNESS_CHECKS => [],
101
+ :DISABLE_SVC_CHECK => %w{host_name service_description},
102
+ :DISABLE_SVC_EVENT_HANDLER => %w{host_name service_description},
103
+ :DISABLE_SVC_FLAP_DETECTION => %w{host_name service_description},
104
+ :DISABLE_SVC_NOTIFICATIONS => %w{host_name service_description},
105
+ :ENABLE_ALL_NOTIFICATIONS_BEYOND_HOST => %w{host_name},
106
+ :ENABLE_CONTACTGROUP_HOST_NOTIFICATIONS => %w{contactgroup_name},
107
+ :ENABLE_CONTACTGROUP_SVC_NOTIFICATIONS => %w{contactgroup_name},
108
+ :ENABLE_CONTACT_HOST_NOTIFICATIONS => %w{contact_name},
109
+ :ENABLE_CONTACT_SVC_NOTIFICATIONS => %w{contact_name},
110
+ :ENABLE_EVENT_HANDLERS => [],
111
+ :ENABLE_FAILURE_PREDICTION => [],
112
+ :ENABLE_FLAP_DETECTION => [],
113
+ :ENABLE_HOSTGROUP_HOST_CHECKS => %w{hostgroup_name},
114
+ :ENABLE_HOSTGROUP_HOST_NOTIFICATIONS => %w{hostgroup_name},
115
+ :ENABLE_HOSTGROUP_PASSIVE_HOST_CHECKS => %w{hostgroup_name},
116
+ :ENABLE_HOSTGROUP_PASSIVE_SVC_CHECKS => %w{hostgroup_name},
117
+ :ENABLE_HOSTGROUP_SVC_CHECKS => %w{hostgroup_name},
118
+ :ENABLE_HOSTGROUP_SVC_NOTIFICATIONS => %w{hostgroup_name},
119
+ :ENABLE_HOST_AND_CHILD_NOTIFICATIONS => %w{host_name},
120
+ :ENABLE_HOST_CHECK => %w{host_name},
121
+ :ENABLE_HOST_EVENT_HANDLER => %w{host_name},
122
+ :ENABLE_HOST_FLAP_DETECTION => %w{host_name},
123
+ :ENABLE_HOST_FRESHNESS_CHECKS => [],
124
+ :ENABLE_HOST_NOTIFICATIONS => %w{host_name},
125
+ :ENABLE_HOST_SVC_CHECKS => %w{host_name},
126
+ :ENABLE_HOST_SVC_NOTIFICATIONS => %w{host_name},
127
+ :ENABLE_NOTIFICATIONS => [],
128
+ :ENABLE_PASSIVE_HOST_CHECKS => %w{host_name},
129
+ :ENABLE_PASSIVE_SVC_CHECKS => %w{host_name service_description},
130
+ :ENABLE_PERFORMANCE_DATA => [],
131
+ :ENABLE_SERVICEGROUP_HOST_CHECKS => %w{servicegroup_name},
132
+ :ENABLE_SERVICEGROUP_HOST_NOTIFICATIONS => %w{servicegroup_name},
133
+ :ENABLE_SERVICEGROUP_PASSIVE_HOST_CHECKS => %w{servicegroup_name},
134
+ :ENABLE_SERVICEGROUP_PASSIVE_SVC_CHECKS => %w{servicegroup_name},
135
+ :ENABLE_SERVICEGROUP_SVC_CHECKS => %w{servicegroup_name},
136
+ :ENABLE_SERVICEGROUP_SVC_NOTIFICATIONS => %w{servicegroup_name},
137
+ :ENABLE_SERVICE_FRESHNESS_CHECKS => [],
138
+ :ENABLE_SVC_CHECK => %w{host_name service_description},
139
+ :ENABLE_SVC_EVENT_HANDLER => %w{host_name service_description},
140
+ :ENABLE_SVC_FLAP_DETECTION => %w{host_name service_description},
141
+ :ENABLE_SVC_NOTIFICATIONS => %w{host_name service_description},
142
+ :PROCESS_FILE => %w{file_name delete},
143
+ :PROCESS_HOST_CHECK_RESULT => %w{host_name status_code plugin_output},
144
+ :PROCESS_SERVICE_CHECK_RESULT => %w{host_name service_description return_code plugin_output},
145
+ :READ_STATE_INFORMATION => [],
146
+ :REMOVE_HOST_ACKNOWLEDGEMENT => %w{host_name},
147
+ :REMOVE_SVC_ACKNOWLEDGEMENT => %w{host_name service_description},
148
+ :RESTART_PROGRAM => [],
149
+ :SAVE_STATE_INFORMATION => [],
150
+ :SCHEDULE_AND_PROPAGATE_HOST_DOWNTIME => %w{host_name start_time end_time fixed trigger_id duration author comment},
151
+ :SCHEDULE_AND_PROPAGATE_TRIGGERED_HOST_DOWNTIME => %w{host_name start_time end_time fixed trigger_id duration author comment},
152
+ :SCHEDULE_FORCED_HOST_CHECK => %w{host_name check_time},
153
+ :SCHEDULE_FORCED_HOST_SVC_CHECKS => %w{host_name check_time},
154
+ :SCHEDULE_FORCED_SVC_CHECK => %w{host_name service_description check_time},
155
+ :SCHEDULE_HOSTGROUP_HOST_DOWNTIME => %w{hostgroup_name start_time end_time fixed trigger_id duration author comment},
156
+ :SCHEDULE_HOSTGROUP_SVC_DOWNTIME => %w{hostgroup_name start_time end_time fixed trigger_id duration author comment},
157
+ :SCHEDULE_HOST_CHECK => %w{host_name check_time},
158
+ :SCHEDULE_HOST_DOWNTIME => %w{host_name start_time end_time fixed trigger_id duration author comment},
159
+ :SCHEDULE_HOST_SVC_CHECKS => %w{host_name check_time},
160
+ :SCHEDULE_HOST_SVC_DOWNTIME => %w{host_name start_time end_time fixed trigger_id duration author comment},
161
+ :SCHEDULE_SERVICEGROUP_HOST_DOWNTIME => %w{servicegroup_name start_time end_time fixed trigger_id duration author comment},
162
+ :SCHEDULE_SERVICEGROUP_SVC_DOWNTIME => %w{servicegroup_name start_time end_time fixed trigger_id duration author comment},
163
+ :SCHEDULE_SVC_CHECK => %w{host_name service_description check_time},
164
+ :SCHEDULE_SVC_DOWNTIME => %w{host_name service_desription start_time end_time fixed trigger_id duration author comment},
165
+ :SEND_CUSTOM_HOST_NOTIFICATION => %w{host_name options author comment},
166
+ :SEND_CUSTOM_SVC_NOTIFICATION => %w{host_name service_description options author comment},
167
+ :SET_HOST_NOTIFICATION_NUMBER => %w{host_name notification_number},
168
+ :SET_SVC_NOTIFICATION_NUMBER => %w{host_name service_description notification_number},
169
+ :SHUTDOWN_PROGRAM => [],
170
+ :START_ACCEPTING_PASSIVE_HOST_CHECKS => [],
171
+ :START_ACCEPTING_PASSIVE_SVC_CHECKS => [],
172
+ :START_EXECUTING_HOST_CHECKS => [],
173
+ :START_EXECUTING_SVC_CHECKS => [],
174
+ :START_OBSESSING_OVER_HOST => %w{host_name},
175
+ :START_OBSESSING_OVER_HOST_CHECKS => [],
176
+ :START_OBSESSING_OVER_SVC => %w{host_name service_description},
177
+ :START_OBSESSING_OVER_SVC_CHECKS => [],
178
+ :STOP_ACCEPTING_PASSIVE_HOST_CHECKS => [],
179
+ :STOP_ACCEPTING_PASSIVE_SVC_CHECKS => [],
180
+ :STOP_EXECUTING_HOST_CHECKS => [],
181
+ :STOP_EXECUTING_SVC_CHECKS => [],
182
+ :STOP_OBSESSING_OVER_HOST => %w{host_name},
183
+ :STOP_OBSESSING_OVER_HOST_CHECKS => [],
184
+ :STOP_OBSESSING_OVER_SVC => %w{host_name service_description},
185
+ :STOP_OBSESSING_OVER_SVC_CHECKS => []
186
+ }.freeze
187
+
188
+ def self.include?(action)
189
+ ACTIONS.keys.include? action.to_sym
190
+ end
191
+
192
+ def self.keys_for(action)
193
+ ACTIONS[action]
194
+ end
195
+ end
196
+
197
+ end
198
+ end
@@ -0,0 +1,100 @@
1
+ require_relative 'configuration/filter'
2
+ require_relative 'configuration/nagios_server'
3
+ require_relative 'configuration/service'
4
+
5
+ module Naginata
6
+ class Configuration
7
+
8
+ def initialize(config = nil)
9
+ @config ||= config
10
+ end
11
+
12
+ def self.env
13
+ @env ||= new
14
+ end
15
+
16
+ def set(key, value)
17
+ config[key] = value
18
+ end
19
+
20
+ def set_if_empty(key, value)
21
+ config[key] = value unless config.has_key? key
22
+ end
23
+
24
+ def fetch(key, default=nil, &block)
25
+ if block_given?
26
+ config.fetch(key, &block)
27
+ else
28
+ config.fetch(key, default)
29
+ end
30
+ end
31
+
32
+ def keys
33
+ config.keys
34
+ end
35
+
36
+ # @ToDo second argment for server specific options
37
+ def nagios_server(name)
38
+ nagios_servers << NagiosServer.new(name)
39
+ end
40
+
41
+ def host(host, options = {})
42
+ ArgumentError "on: is required option" unless options[:on]
43
+ nagios = options[:on]
44
+ services << Host.new(host, on: nagios)
45
+ Array(options[:services]).each { |s| services << Service.new(s, host: host, on: nagios) }
46
+ end
47
+
48
+ def timestamp
49
+ @timestamp ||= Time.now.utc
50
+ end
51
+
52
+ def add_filter(type, values)
53
+ filters << Filter.new(type, values)
54
+ end
55
+
56
+ def filter list
57
+ filters.reduce(list) { |l,f| f.filter l }
58
+ end
59
+
60
+ def filter_service list
61
+ filters.reduce(list) { |l,f| f.filter_service l }
62
+ end
63
+
64
+ def nagios_servers
65
+ @nagios_servers ||= []
66
+ end
67
+
68
+ def services
69
+ @services ||= []
70
+ end
71
+
72
+ def backend
73
+ SSHKit
74
+ end
75
+
76
+ def configure_backend
77
+ backend.configure do |sshkit|
78
+ sshkit.format = fetch(:format)
79
+ sshkit.output_verbosity = fetch(:log_level)
80
+ sshkit.backend = fetch(:sshkit_backend, SSHKit::Backend::Netssh)
81
+ sshkit.backend.configure do |backend|
82
+ backend.pty = fetch(:pty)
83
+ backend.ssh_options = (backend.ssh_options || {}).merge(fetch(:ssh_options,{}))
84
+ end
85
+ end
86
+ end
87
+
88
+
89
+ private
90
+
91
+ def filters
92
+ @filters ||= []
93
+ end
94
+
95
+ def config
96
+ @config ||= Hash.new
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,78 @@
1
+ require 'naginata/configuration'
2
+ require 'naginata/configuration/service'
3
+
4
+ module Naginata
5
+ class Configuration
6
+ class Filter
7
+ def initialize type, values = nil
8
+ raise "Invalid filter type #{type}" unless [:nagios_server, :host, :service].include? type
9
+ av = Array(values).dup
10
+ @mode = case
11
+ when av.include?(:all) then :all
12
+ else type
13
+ end
14
+ @rex = case @mode
15
+ when :all
16
+ nil # this creates a filter matching any string
17
+ when :nagios_server, :host
18
+ av.map!{|v| (v.is_a?(String) && v =~ /^(?<name>[-A-Za-z0-9.]+)(,\g<name>)*$/) ? v.split(',') : v }
19
+ av.flatten!
20
+ av.map! do |v|
21
+ case v
22
+ when Regexp then v
23
+ else
24
+ vs = v.to_s
25
+ vs =~ /^[-A-Za-z0-9.]+$/ ? vs : Regexp.new(vs)
26
+ end
27
+ end
28
+ Regexp.union av
29
+ when :service
30
+ av.map!{|v| v.is_a?(String) ? v.split(',') : v }
31
+ av.flatten!
32
+ av.map! do |v|
33
+ case v
34
+ when Regexp then v
35
+ else
36
+ vs = v.to_s
37
+ Regexp.new(vs)
38
+ end
39
+ end
40
+ Regexp.union av
41
+ else
42
+ raise "unprocessable type"
43
+ end
44
+ end
45
+
46
+ def filter nagios_servers
47
+ a = Array(nagios_servers)
48
+ case @mode
49
+ when :all
50
+ a
51
+ when :nagios_server
52
+ a.select { |ns| @rex.match ns.to_s }
53
+ else
54
+ a
55
+ end
56
+ end
57
+
58
+ def filter_service services
59
+ a = Array(services)
60
+ case @mode
61
+ when :all
62
+ a
63
+ when :nagios_server
64
+ a.select { |s| @rex.match s.nagios }
65
+ when :host
66
+ a.select { |s| @rex.match s.hostname }
67
+ when :service
68
+ # Ignore host monitors
69
+ a.reject { |s| s.description == :ping }
70
+ .select { |s| @rex.match s.description }
71
+ else
72
+ a
73
+ end
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,83 @@
1
+ require 'sshkit'
2
+
3
+ module Naginata
4
+ class Configuration
5
+ class NagiosServer < SSHKit::Host
6
+ extend Forwardable
7
+ def_delegators :properties, :fetch, :set
8
+
9
+ def with(properties)
10
+ properties.each { |key, value| add_property(key, value) }
11
+ self
12
+ end
13
+
14
+ def properties
15
+ @properties ||= Properties.new
16
+ end
17
+
18
+ def netssh_options
19
+ @netssh_options ||= super.merge( fetch(:ssh_options) || {} )
20
+ end
21
+
22
+ def matches?(other)
23
+ hostname == other.hostname
24
+ end
25
+
26
+ private
27
+
28
+ def add_property(key, value)
29
+ if respond_to?("#{key}=")
30
+ send("#{key}=", value)
31
+ else
32
+ set(key, value)
33
+ end
34
+ end
35
+
36
+ class Properties
37
+
38
+ def initialize
39
+ @properties = {}
40
+ end
41
+
42
+ def set(key, value)
43
+ pval = @properties[key]
44
+ if pval.is_a? Hash and value.is_a? Hash
45
+ pval.merge!(value)
46
+ elsif pval.is_a? Array and value.is_a? Array
47
+ pval.concat value
48
+ else
49
+ @properties[key] = value
50
+ end
51
+ end
52
+
53
+ def fetch(key)
54
+ @properties[key]
55
+ end
56
+
57
+ def respond_to?(method, include_all=false)
58
+ @properties.has_key?(method)
59
+ end
60
+
61
+ def keys
62
+ @properties.keys
63
+ end
64
+
65
+ def method_missing(key, value=nil)
66
+ if value
67
+ set(lvalue(key), value)
68
+ else
69
+ fetch(key)
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def lvalue(key)
76
+ key.to_s.chomp('=').to_sym
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+ end
83
+ end