Class: ProcessWanker::Application

Inherits:
Object
  • Object
show all
Includes:
Log
Defined in:
lib/pw_app.rb

Overview

the main ProcessWanker application

Constant Summary collapse

DEFAULT_WAIT =
60
@@display_mutex =
Mutex.new()

Constants included from Log

Log::DEBUG, Log::ERROR, Log::INFO, Log::WARN

Instance Method Summary collapse

Methods included from Log

debug, error, info, log, set_level, warn

Constructor Details

#initializeApplication

Returns a new instance of Application.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/pw_app.rb', line 60

def initialize()
 
#
# parse options
#
		
parse_opts()
		
#
# generate certs/keys?
#
		
if(@options[:generate_ca])
	generate_ca()
	exit
end

if(@options[:generate_user])
	generate_user()
	exit
end

Config::set_config_path(@options[:config]) if(@options[:config])

#
# run as daemon?
#

if(@options[:daemon])
	start_daemon()
	exit
end

#
# list known clusters/hosts?
#

cp=Config::get_config_path
raise "no such config file: #{cp}" if(!File.exist?(cp))
@config=Config::load_config(cp)  # .new( :client, File.read(cp) )
raise "no client config provided" if(!@config.client)
if(@options[:list_clusters])
	list_clusters()
	exit
end

#
# ok... it's a remote command 
#

execute_remote()

end

Instance Method Details

#display_response(resp, host) ⇒ Object



490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
# File 'lib/pw_app.rb', line 490

def display_response(resp,host)

	@@display_mutex.synchronize do
		
     if(resp[:services])
			puts
			printf("[%s]",host.name)
			if(host.tags.length>0)
				puts " - (#{host.tags.join(",")})" 
			else
				puts
			end
			printf("    %-30s %-30s %-20s %s\n","name","state","want-state","tags")
			groups=resp[:services].values.map { |x| x[:group_name] }.uniq.sort
			groups.each do |grp|
				puts "  [group: #{grp}]" if(groups.length > 1)
				services=resp[:services].values.select { |x| x[:group_name] == grp }
				services.sort! { |a,b| a[:name] <=> b[:name] }
				services.each do |s|
					show=s[:show_state]
					show << " (suppressed)" if(s[:suppress])
					printf("    %-30s %-30s %-20s %s\n",
						s[:name],
						show,
						s[:want_state],
						s[:tags].join(" ")
					)
				end
			end			
			
			puts
		end
	
		if(resp[:counts])
				resp[:counts].keys.sort.each do |name|
					printf("%-50s %d\n",name,resp[:counts][name])
				end
		end
		
	end
	
end

#execute_host_action(host, action) ⇒ Object



461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
# File 'lib/pw_app.rb', line 461

def execute_host_action(host,action)
	begin
		client=NetClient.new(host)
		req=action.merge(
			{
				:wait				=> @options[:wait],
				:sequential	=> @options[:sequential]
			})
		client.send_msg(req)
		resp=client.wait
	rescue Exception => e
		puts("[#{host.name}]: connection failure")
		debug(e.message)
		e.backtrace { |x| debug(x) }
	end

	if(resp)
		display_response(resp,host)			
	end
end

#execute_remoteObject



411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
# File 'lib/pw_app.rb', line 411

def execute_remote()
	
	#
	# match hosts
	#
	
	hosts=match_hosts(@options[:host],@options[:cluster])
	
	#
	# set defaults
	#
	
	if(@options[:actions].length == 0)
		@options[:actions] << { :cmd => :list, :spec => "all" }
	end
	@options[:wait] ||= DEFAULT_WAIT

	#
	# execute each action sequentially
	#
	
	@options[:actions].each do |action|
	
		#
		# hit each host in turn, or concurrently
		#			
	
		if(@options[:sequential])
			hosts.each do |host|
				execute_host_action(host,action)
			end
		else
			threads=[]
			hosts.each do |host|
				threads << Thread.new do 
					execute_host_action(host,action)
				end
			end
			threads.each { |t| t.join }
		end
	end
	
end

#generate_caObject



258
259
260
261
262
263
264
265
266
267
268
# File 'lib/pw_app.rb', line 258

def generate_ca()
	raise "no --output-prefix specified" if(!@options[:output_prefix])
	pass=nil
	if(@options[:require_passphrase])
		pass=HighLine.new.ask("Password (for encrypting output key) : ") { |x| x.echo=false }.strip
		p2=HighLine.new.ask("(verify)                             : ") { |x| x.echo=false }.strip
		raise "passwords don't match" if(pass != p2)
	end
	
	ProcessWanker::NetUtil::generate_ca(@options[:output_prefix],pass)
end

#generate_userObject



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/pw_app.rb', line 276

def generate_user()
	raise "no --output-prefix specified" if(!@options[:output_prefix])
	raise "no --ca-prefix specified" if(!@options[:ca_prefix])
	pass=nil
	if(@options[:require_passphrase])
		pass=HighLine.new.ask("Password (for encrypting output key) : ") { |x| x.echo=false }.strip
		p2=HighLine.new.ask("(verify)                             : ") { |x| x.echo=false }.strip
		raise "passwords don't match" if(pass != p2)
	end
	
	ProcessWanker::NetUtil::generate_cert(
		@options[:ca_prefix],
		@options[:output_prefix],
		@options[:generate_user],
		pass)
		
end

#list_clustersObject



386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/pw_app.rb', line 386

def list_clusters()
	
	# ensure that at least default cluster exists
	@config.client.get_cluster("default")
	
	puts "clusters:"
	@config.client.clusters.clusters.each do |cname,cluster|
		puts " [#{cname}]"
		c=cluster
		c.hosts.each do |hname,h|
			printf("    %-30s",hname)
			printf("    %-30s","#{h.hostname}:#{h.port}")
			h.tags.each { |t| printf("%s ",t) }
			puts
		end
	end
	exit
end

#match_hosts(hostspec, cluster_name) ⇒ Object



370
371
372
373
374
375
376
377
378
# File 'lib/pw_app.rb', line 370

def match_hosts(hostspec,cluster_name)
	cluster_name ||= "default"
	cluster=@config.client.get_cluster(cluster_name)
	raise "no such cluster: #{cluster_name}" if(!cluster)

	hosts=[]
	cluster.hosts.each { |k,v| hosts << v if(!hostspec || v.matches_spec(hostspec)) }
	hosts
end

#parse_optsObject



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/pw_app.rb', line 120

def parse_opts()

	options={}
	options[:actions]=[]

	optparse=OptionParser.new do |opts|
	
		opts.on(nil,'--help','Display this screen') do
			puts
			puts opts
			puts
			puts "  SERVICE_SPEC can be a 'all', a name, a regular expression, or a tag - eg. tag:db"
			puts "  It can also be a comma-separated list of all of the above. Prefixing a string/regex/tag with"
			puts "  a tilde (~) has the meaning 'except'."
			puts
			exit
		end
	
		opts.on("-l","--list [SERVICE_SPEC]","Show the status of the given service(s)") do |v|
			options[:actions] << { :cmd => :list, :spec => v || "all" }
		end

		opts.on("-s","--start SERVICE_SPEC","Start the given service(s)") do |v|
			options[:actions] << { :cmd => :start, :spec => v || "all" }
		end

		opts.on("-k","--kill SERVICE_SPEC","Stop the given service(s)") do |v|
			options[:actions] << { :cmd => :stop, :spec => v || "all" }
		end
	
		opts.on("-i","--ignore SERVICE_SPEC","Ignore the given service(s)") do |v|
			options[:actions] << { :cmd => :ignore, :spec => v || "all" }
		end
	
		opts.on("--stop SERVICE_SPEC","Stop the given service(s)") do |v|
			options[:actions] << { :cmd => :stop, :spec => v || "all" }
		end
	
		opts.on("-r","--restart SERVICE_SPEC","Restart the given service(s)") do |v|
			options[:actions] << { :cmd => :restart, :spec => v || "all" }
		end

		opts.on("-w","--wait SECS",Integer,"Wait SECS seconds for service(s) to reach requested state, default (#{DEFAULT_WAIT})") do |v|
			options[:wait] = v
		end

		opts.on("--sequential [DELAY_SECS]",Float,"Execute all operations sequentially, with optional delay between them") do |v|
			options[:sequential]=v || 0.0
		end

		opts.on("--reload","Reload configuration") do
			options[:actions] << { :cmd => :reload }
		end
	
		opts.on("--terminate","Terminate daemon") do
			options[:actions] << { :cmd => :terminate }
		end
	
		opts.on("-c","--cluster NAME","Select a cluster to control") do |v|
			options[:cluster]=v
		end
	
		opts.on("-h","--host HOST","Select a single host to control") do |v|
			options[:host]=v
		end

		opts.on("-g","--config FILE","Specify configuration file to use (#{Config::get_config_path})") do |v|
			options[:config]=v
		end
	
		opts.on("--list-clusters","Display a list of known clusters and hosts") do
			options[:list_clusters]=true
		end
	
		opts.on("-d","--daemon","Run in the background as a server") do
			options[:daemon]=true
		end
	
		opts.on("-f","--foreground","Don't detach - stay in the foreground (useful only with -d option)") do
			options[:foreground]=true
		end
	
		opts.on("--notboot","Don't start all local services automatically (useful only with the -d option)") do
			options[:notboot]=true
		end
	
		opts.on("--generate-ca","Generate CA cert/key (requires --output-prefix)") do |v|
			options[:generate_ca]=v
		end
	
		opts.on("--output-prefix PATH","Specify output file prefix for .crt and .key") do |v|
			options[:output_prefix]=v
		end
	
		opts.on("--ca-prefix PATH","Specify input file location prefix for the CA certificate and key") do |v|
			options[:ca_prefix]=v
		end
	
		opts.on("--require-passphrase","Use a passphrase to encrypt any generated private keys") do
			options[:require_passphrase]=true
		end
	
		opts.on("--generate-user NAME","Generate client cert/key - requires (--ca-prefix and --output-prefix)") do |v|
			options[:generate_user]=v
		end
	
		opts.on("--debug","Debug hook") do |v|
			options[:actions] << { :cmd => :debug }
		end
	
		opts.on("--log FILE","Log file (for pw output only)") do |v|
			options[:log_file] = v
      end
      
		opts.on("--verbose","Debugging enabled") do |v|
			Log::set_level(Log::DEBUG)
		end
	
	
	end

	extra=optparse.parse!

	if(extra.length > 0)
		puts "warning: ignoring extra params: #{extra.join(" ")}"
	end

	@options=options
		
	
end

#start_daemonObject



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/pw_app.rb', line 300

def start_daemon
	
	info("starting daemon")			

	# create servicemgr
	ServiceMgr.new()

	# load daemon config
    puts Config::get_config_path()
#			@config=Config.new( :daemon, File.read( Config::get_config_path ) )
	@config=Config::load_config( Config::get_config_path )  # .new( :client, File.read(cp) )
	raise "no daemon config provided" if(!@config.daemon)

	# apply config
	ServiceMgr::instance().apply_config(@config,@options[:notboot])

	# daemonize?			
	if(!@options[:foreground])

      puts "Going to background..."
      
      Process.fork do
        
  			# start net server
  			NetServer.new(@config)


				# redirect inputs/outputs
				file=@options[:log_file] || @config.daemon.log_file || "/var/log/pw.log"
        puts "logging to #{file}"
        begin
          FileUtils.mkdir_p( File.dirname(file) )
          File.open(file,"a") { |x| x.puts "ProcessWanker starts" }
          STDERR.reopen(file,"a")
          STDOUT.reopen(file,"a")
  				STDIN.reopen("/dev/null")
        rescue Exception => e
          raise "unable to open log file #{file}"
        end
        
  			Log::set_level(Log::DEBUG)
        
        # start new session
        Process.setsid()
        
  			# run
  			ServiceMgr::instance().run()
        
      end
      
    else
      
			# start net server
			NetServer.new(@config)


      # just run in foreground
      ServiceMgr::instance().run()
      
    end
	

end