naginata 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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