processwanker 0.0.7

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,103 @@
1
+ ############################################################################
2
+ #
3
+ # config_smtp.rb
4
+ #
5
+ # smtp block parser:
6
+ #
7
+ # smtp {
8
+ # server "smtp.mymailprovider.com"
9
+ # port 25
10
+ # secure "starttls" ("ssl")
11
+ # to_addr "joe-admin@foobar.com"
12
+ # from_addr "pw@mydomain.com"
13
+ # userid "fred"
14
+ # password "mypass123"
15
+ # auth_method "plain" ("login", "cram_md5")
16
+ # }
17
+ #
18
+ ############################################################################
19
+
20
+ require 'ipaddr'
21
+ require 'openssl'
22
+ require 'config'
23
+
24
+ module ProcessWanker
25
+
26
+ ############################################################################
27
+ #
28
+ #
29
+ #
30
+ ############################################################################
31
+
32
+ class ConfigSMTP < ConfigNode
33
+
34
+ attr_accessor :server
35
+ attr_accessor :port
36
+ attr_accessor :secure
37
+ attr_accessor :to_addrs
38
+ attr_accessor :from_addr
39
+ attr_accessor :userid
40
+ attr_accessor :password
41
+ attr_accessor :auth_method
42
+
43
+ def initialize()
44
+ @to_addrs=[]
45
+ port=25
46
+ ssl=false
47
+ from_domain=`hostname`.strip
48
+ end
49
+
50
+ end
51
+
52
+ ############################################################################
53
+ #
54
+ #
55
+ #
56
+ ############################################################################
57
+
58
+ class ConfigSMTPBuilder < Builder
59
+
60
+ def server(v)
61
+ @config.server=v
62
+ end
63
+
64
+ def port(v)
65
+ @config.port=v.to_i
66
+ end
67
+
68
+ def secure(v)
69
+ v=v.to_sym
70
+ raise "bad smtp secure setting" if(![:ssl,:starttls,:tls].include?(v))
71
+ @config.secure=v
72
+ end
73
+
74
+ def from_addr(v)
75
+ @config.from_addr=v
76
+ end
77
+
78
+ def to_addrs(*v)
79
+ @config.to_addrs += v
80
+ end
81
+
82
+ def userid(v)
83
+ @config.userid=v
84
+ end
85
+
86
+ def password(v)
87
+ @config.password=v
88
+ end
89
+
90
+ def auth_method(v)
91
+ @config.auth_method=v.to_sym
92
+ end
93
+
94
+ end
95
+
96
+ ############################################################################
97
+ #
98
+ #
99
+ #
100
+ ############################################################################
101
+
102
+ end
103
+
data/lib/events.rb ADDED
@@ -0,0 +1,224 @@
1
+ ############################################################################
2
+ #
3
+ # events.rb
4
+ #
5
+ # handle dispatching of events to event hooks
6
+ #
7
+ ############################################################################
8
+
9
+ require 'log'
10
+ require 'util'
11
+ require 'net/smtp'
12
+
13
+ module ProcessWanker
14
+
15
+ ############################################################################
16
+ #
17
+ # basic event
18
+ #
19
+ ############################################################################
20
+
21
+ class Event
22
+
23
+ ############################################################################
24
+ #
25
+ # properties
26
+ #
27
+ # a set of useful information for the handler to process
28
+ #
29
+ ############################################################################
30
+
31
+ attr_accessor :type
32
+ attr_accessor :service
33
+ attr_accessor :service_name
34
+ attr_accessor :service_stats
35
+ attr_accessor :service_state
36
+ attr_accessor :time
37
+
38
+ ############################################################################
39
+ #
40
+ #
41
+ #
42
+ ############################################################################
43
+
44
+ def self.dispatch(type,service)
45
+
46
+ Log::debug("dispatching event #{type} for service #{service.name}")
47
+
48
+ e=Event.new()
49
+ e.type=type
50
+ e.service=service
51
+ e.service_name=service.name
52
+ e.service_stats={}
53
+ # e.service_stats=service.stats
54
+ e.service_state=service.current_state
55
+ e.time=Time.now
56
+
57
+ # build array of hooks, from innermost to outermost
58
+ stop=false
59
+ hooks_lists=service.config_node.find_attributes("hooks")
60
+ hooks_lists.each do |hooks|
61
+ hooks.each do |hook|
62
+
63
+ next unless(hook.pattern == type)
64
+ context=EventHookContext.new
65
+ context.service=service
66
+ context.event=e
67
+
68
+ ProcessWanker::with_logged_rescue("hook for event #{e.type} #{e.service.name}") do
69
+ context.instance_eval(&hook.block)
70
+ end
71
+
72
+ stop=context.should_stop_hooks
73
+ if(stop)
74
+ break
75
+ end
76
+ end
77
+ break if(stop)
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+
84
+
85
+ ############################################################################
86
+ #
87
+ # EventHookContext is the context in which a hook executes
88
+ #
89
+ # it has an event member, and a set of helper functions for handling
90
+ # notifications, etc...
91
+ #
92
+ ############################################################################
93
+
94
+ class EventHookContext
95
+
96
+ include Log
97
+
98
+ attr_accessor :service
99
+ attr_accessor :event
100
+ attr_accessor :should_stop_hooks
101
+
102
+ ############################################################################
103
+ #
104
+ # helpers
105
+ #
106
+ ############################################################################
107
+
108
+ def email()
109
+
110
+ # find smtp config
111
+ smtp_config=service.config_node.find_attributes("smtp").first
112
+ if(!smtp_config)
113
+ Log::error("attempting to send an email, but no SMTP configuration found")
114
+ return
115
+ end
116
+ from_user,from_domain=smtp_config.from_addr.split("@")
117
+
118
+ # construct email
119
+ @email_addresses=smtp_config.to_addrs.clone
120
+ @email_subject="ProcessWanker event: #{@event.type} from #{event.service_name}"
121
+ @email_body="At #{@event.time}, #{event.service_name} triggered a <#{@event.type}> event\n"
122
+ @email_body << "The current state is #{@event.service_state}.\n"
123
+ @email_body << "Service stats:\n"
124
+ @event.service_stats.each do |k,v|
125
+ @email_body << " #{sprintf("%-20s",k)} : #{v}\n"
126
+ end
127
+ @email_body << "\n"
128
+
129
+ # allow configuration to override some values
130
+ if(block_given?)
131
+ yield
132
+ end
133
+
134
+ # send the email
135
+ Log::debug("SENDING EMAIL: #{@email_addresses.inspect}")
136
+ Log::debug(puts "Subject: #{@email_subject}")
137
+ Log::debug(puts "Body: \n#{@email_body}")
138
+
139
+ msg="From: #{smtp_config.from_addr}\n"
140
+ msg << "To: #{@email_addresses.join(",")}\n"
141
+ msg << "Subject: #{@email_subject}\n\n"
142
+ msg << @email_body
143
+
144
+ #
145
+ # TODO: consider putting this in a different thread, or at least adding a timeout
146
+ #
147
+
148
+ ProcessWanker::with_logged_rescue("sending emails via #{smtp_config.server}") do
149
+
150
+ smtp=Net::SMTP.new(smtp_config.server,smtp_config.port)
151
+
152
+ if(smtp_config.secure == :tls || smtp_config.secure == :ssl)
153
+ smtp.enable_tls
154
+ elsif(smtp_config.secure == :starttls)
155
+ smtp.enable_starttls
156
+ end
157
+
158
+ smtp.start(from_domain,
159
+ smtp_config.userid,
160
+ smtp_config.password,
161
+ smtp_config.auth_method)
162
+ @email_addresses.each do |to|
163
+ ProcessWanker::with_logged_rescue("sending email to #{to}") do
164
+ smtp.send_message(msg,smtp_config.from_addr,to)
165
+ end
166
+ end
167
+ smtp.finish
168
+ end
169
+
170
+ Log::debug("sent email")
171
+
172
+ end
173
+
174
+ ############################################################################
175
+ #
176
+ # email helpers
177
+ #
178
+ ############################################################################
179
+
180
+ def email_to_addrs(*addr)
181
+ @email_addresses += addr
182
+ end
183
+
184
+ def email_subject(subject)
185
+ @email_subject = subject
186
+ end
187
+
188
+ def email_body(body)
189
+ @email_body=body
190
+ end
191
+
192
+ ############################################################################
193
+ #
194
+ # general helpers
195
+ #
196
+ ############################################################################
197
+
198
+ def stop_hooks
199
+ @should_stop_hooks=true
200
+ end
201
+
202
+ def start
203
+ info("hook requested service start")
204
+ @event.service.set_want_state(:up)
205
+ end
206
+
207
+ def stop
208
+ info("hook requested service stop")
209
+ @event.service.set_want_state(:down)
210
+ end
211
+
212
+ def restart
213
+ info("hook requested service restart")
214
+ @event.service.set_want_state(:restart)
215
+ end
216
+
217
+ def ignore
218
+ info("hook requested service ignore")
219
+ @event.service.set_want_state(:ignore)
220
+ end
221
+
222
+ end
223
+
224
+ end
data/lib/log.rb ADDED
@@ -0,0 +1,88 @@
1
+ ############################################################################
2
+ #
3
+ # log.rb
4
+ #
5
+ ############################################################################
6
+
7
+ require 'thread'
8
+
9
+ module ProcessWanker
10
+
11
+ ############################################################################
12
+ #
13
+ #
14
+ #
15
+ ############################################################################
16
+
17
+ module Log
18
+
19
+ @@mutex=Mutex.new
20
+
21
+ ############################################################################
22
+ #
23
+ #
24
+ #
25
+ ############################################################################
26
+
27
+ DEBUG = 0
28
+ INFO = 1
29
+ WARN = 2
30
+ ERROR = 3
31
+
32
+ @@level=INFO
33
+
34
+ ############################################################################
35
+ #
36
+ #
37
+ #
38
+ ############################################################################
39
+
40
+ def set_level(level)
41
+ @@level=level
42
+ end
43
+ module_function :set_level
44
+
45
+ ############################################################################
46
+ #
47
+ #
48
+ #
49
+ ############################################################################
50
+
51
+ def log(msg,level=INFO)
52
+ return unless(level >= @@level)
53
+ @@mutex.synchronize do
54
+ STDOUT.puts "[#{level}] [#{Time.new}] #{msg}"
55
+ STDOUT.flush
56
+ end
57
+ end
58
+ module_function :log
59
+
60
+ ############################################################################
61
+ #
62
+ #
63
+ #
64
+ ############################################################################
65
+
66
+ def error(msg)
67
+ log(msg,ERROR)
68
+ end
69
+ module_function :error
70
+ def warn(msg)
71
+ log(msg,WARN)
72
+ end
73
+ module_function :warn
74
+ def info(msg)
75
+ log(msg,INFO)
76
+ end
77
+ module_function :info
78
+ def debug(msg)
79
+ log(msg,DEBUG)
80
+ end
81
+ module_function :debug
82
+
83
+ end
84
+
85
+
86
+ include Log
87
+
88
+ end
@@ -0,0 +1,189 @@
1
+ ############################################################################
2
+ #
3
+ # net_api.rb
4
+ #
5
+ # utilities for constructing and executing network requests
6
+ #
7
+ ############################################################################
8
+
9
+ require 'log'
10
+ require 'util'
11
+
12
+ module ProcessWanker
13
+
14
+ module NetApi
15
+
16
+ include Log
17
+
18
+ ############################################################################
19
+ #
20
+ # a Net request is a hash that has a :cmd field, and a bunch of other parameters.
21
+ #
22
+ # :cmd can be
23
+ #
24
+ # - :start [spec]
25
+ # - :restart [spec]
26
+ # - :stop [spec]
27
+ # - :list [spec]
28
+ #
29
+ # other args:
30
+ #
31
+ # :spec - spec to match processes against
32
+ #
33
+ # :sequential - if present, execute each service operation sequentially,
34
+ # rather than in parallel - and delay :sequential seconds
35
+ # between each service.
36
+ #
37
+ # :wait - if present max# of secs to wait for each process to reach
38
+ # desired state.
39
+ #
40
+ ############################################################################
41
+
42
+ ############################################################################
43
+ #
44
+ # execute a command from a remote client. called from the client's
45
+ # read thread.
46
+ #
47
+ ############################################################################
48
+
49
+ def execute(cmd,connection)
50
+
51
+ Log::info("received command #{cmd.inspect} from #{connection.user}")
52
+
53
+ # execute
54
+ if(cmd[:cmd] == :reload)
55
+
56
+ ServiceMgr.instance.reload_config=true
57
+
58
+ # wait for service manager to tick
59
+ ticks=ServiceMgr.instance.tick_count
60
+ sleep(0.5) while(ServiceMgr.instance.tick_count-ticks < 2)
61
+
62
+ elsif(cmd[:cmd] == :debug)
63
+
64
+ cls={}
65
+ GC.start
66
+ ObjectSpace.each_object do |o|
67
+ cls[o.class.name] ||= 0
68
+ cls[o.class.name] += 1
69
+ end
70
+ return({ :counts => cls })
71
+
72
+ elsif(cmd[:cmd] == :terminate)
73
+
74
+ ServiceMgr.instance.terminate=true
75
+ connection.inform("terminating")
76
+ return(nil)
77
+
78
+ elsif([:stop,:restart,:start,:ignore].include?(cmd[:cmd]))
79
+
80
+ # get hash of services
81
+ services=ServiceMgr::instance.match_services(cmd[:spec] || "all")
82
+
83
+ threads=[]
84
+ services.keys.sort.each do |sn|
85
+
86
+ service=services[sn]
87
+
88
+ if(cmd[:sequential])
89
+ execute_cmd_single(cmd,service,connection)
90
+ sleep(cmd[:sequential])
91
+ else
92
+ threads << Thread.new do
93
+ ProcessWanker::with_logged_rescue("execute_cmd_single") do
94
+ execute_cmd_single(cmd,service,connection)
95
+ end
96
+ end
97
+ end
98
+
99
+ end
100
+
101
+ threads.each { |t| t.join }
102
+
103
+ end
104
+
105
+ # construct response
106
+ services ||= ServiceMgr::instance.match_services(cmd[:spec] || "all")
107
+
108
+ resp={ :services => {} }
109
+ services.keys.sort.each do |sn|
110
+ service=services[sn]
111
+ resp[:services][sn] =
112
+ {
113
+ :name => service.name,
114
+ :group_name => service.group_name,
115
+ :tags => service.params[:tags],
116
+ :want_state => service.want_state,
117
+ :show_state => service.show_state,
118
+ :suppress => service.suppress
119
+ }
120
+ end
121
+ resp
122
+ end
123
+
124
+ module_function :execute
125
+
126
+ ############################################################################
127
+ #
128
+ # execute a command from a remote client. called from the client's
129
+ # read thread.
130
+ #
131
+ ############################################################################
132
+
133
+ def execute_cmd_single(cmd,service,connection)
134
+
135
+ connection.inform("send #{cmd[:cmd].inspect} to #{service.name}")
136
+
137
+ want={
138
+ :start => :up,
139
+ :stop => :down,
140
+ :restart => :restart,
141
+ :ignore => :ignore
142
+ }[cmd[:cmd]]
143
+ if(want)
144
+ Log::debug("sending #{want} to #{service.name}")
145
+ service.set_want_state(want)
146
+ end
147
+
148
+ wait=cmd[:wait]
149
+ return(false) if(!wait)
150
+ start_time=Time.now
151
+
152
+ # wait for service manager to tick at least once
153
+ ticks=ServiceMgr.instance.tick_count
154
+ sleep(0.5) while(ServiceMgr.instance.tick_count == ticks)
155
+
156
+ return(true) if(want == :ignore)
157
+
158
+
159
+ # wait for process to be in desired state
160
+ while(service.current_state != service.want_state || service.want_state == :restart)
161
+ sleep(0.5)
162
+
163
+ # timeout?
164
+ if((Time.now - start_time) >= wait)
165
+ connection.inform("timed out waiting for #{service.name}")
166
+ return(false)
167
+ end
168
+ end
169
+
170
+ true
171
+ end
172
+
173
+ module_function :execute_cmd_single
174
+
175
+ ############################################################################
176
+ #
177
+ #
178
+ #
179
+ ############################################################################
180
+
181
+ ############################################################################
182
+ #
183
+ #
184
+ #
185
+ ############################################################################
186
+
187
+ end
188
+
189
+ end
@@ -0,0 +1,107 @@
1
+ ############################################################################
2
+ #
3
+ # net_client.rb
4
+ #
5
+ # outgoing client connection
6
+ #
7
+ ############################################################################
8
+
9
+ require 'openssl'
10
+ require 'config'
11
+ require 'config_client_cluster'
12
+ require 'config_client_host'
13
+ require 'config_client'
14
+ require 'config_auth'
15
+ require 'net_util'
16
+ require 'socket'
17
+ require 'net_connection'
18
+ require 'thread'
19
+
20
+ module ProcessWanker
21
+
22
+ ############################################################################
23
+ #
24
+ #
25
+ #
26
+ ############################################################################
27
+
28
+ class NetClient < NetConnection
29
+
30
+ ############################################################################
31
+ #
32
+ #
33
+ #
34
+ ############################################################################
35
+
36
+ def initialize(cfg_host)
37
+
38
+ @host=cfg_host
39
+ @response=nil
40
+
41
+ # find the real config...
42
+ # auth=cfg_host.auth || cfg_host.parent.auth || cfg_host.parent.parent.auth
43
+ auth=cfg_host.get_auth
44
+ if(!auth)
45
+ raise "could not find auth"
46
+ end
47
+
48
+ @ca_cert=auth.ca_cert
49
+ @context=OpenSSL::SSL::SSLContext.new
50
+ @context.cert=auth.my_cert
51
+ @context.key=auth.my_key
52
+ @context.verify_mode=OpenSSL::SSL::VERIFY_PEER
53
+ @context.verify_callback=proc do |preverify_ok,ssl_context|
54
+ verify_peer(preverify_ok,ssl_context)
55
+ end
56
+
57
+ @tcp_client=TCPSocket.new(cfg_host.hostname,cfg_host.port)
58
+ ssl_connection=OpenSSL::SSL::SSLSocket.new(@tcp_client,@context)
59
+ ssl_connection.connect
60
+ super(ssl_connection)
61
+ end
62
+
63
+ ############################################################################
64
+ #
65
+ #
66
+ #
67
+ ############################################################################
68
+
69
+ def verify_peer(preverify_ok,ssl_context)
70
+ if(!ssl_context.current_cert.verify(@ca_cert.public_key))
71
+ info("server certificate rejected")
72
+ return(false)
73
+ end
74
+ true
75
+ end
76
+
77
+ ############################################################################
78
+ #
79
+ #
80
+ #
81
+ ############################################################################
82
+
83
+ def on_msg(msg)
84
+ super(msg)
85
+ if(msg[:info])
86
+ puts "==[#{@host.name}] #{msg[:info]}"
87
+ end
88
+ if(msg[:done])
89
+ @response=msg
90
+ disconnect()
91
+ end
92
+ end
93
+
94
+ ############################################################################
95
+ #
96
+ #
97
+ #
98
+ ############################################################################
99
+
100
+ def wait
101
+ super
102
+ @response
103
+ end
104
+
105
+ end
106
+
107
+ end