fissher 1.0.2

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.
data/README.rdoc ADDED
@@ -0,0 +1,39 @@
1
+
2
+ == Fissher
3
+
4
+ Fissher is a simple utility to run commands on multiple servers, powered by Net::SSH::Multi.
5
+
6
+ === Usage
7
+
8
+ fissher [flags] [command]
9
+ -G Hostgroup Execute command on all hosts listed in the JSON config for the
10
+ specified group.
11
+ -H Host1,Host2 Execute command on hosts listed on the command line
12
+ -g jumpbox Manually specify/override a jump server, if necessary.
13
+ -s Execute the provided commands with sudo.
14
+ -u username Manually specify/override username to connect with.
15
+ -p Use password based authentication, specified via STDIN
16
+ -c config.json Manually specify the path to your fissher config file
17
+ -n num Number of concurrent connections. Enter 0 for unlimited.
18
+
19
+ === Installation
20
+
21
+ ==== Via Rubygems (Recommended)
22
+
23
+ Simply run the following:
24
+
25
+ gem install fissher
26
+
27
+ ==== Manual
28
+
29
+ * Place the code into a directory by either unarchiving it or checking it out via git.
30
+ * Run bundler to install the dependency gems (getopt, net-ssh-multi, net-ssh-session, json, highline)
31
+
32
+ === JSON Config file
33
+
34
+ This file, by default, lives in the etc directory one level below where
35
+ the main script lives. You can specify it manually with the -c option.
36
+
37
+ There is a sample configuration file provided with the script that displays
38
+ all of the currently configurable options. Any variables can be omitted
39
+ or overridden with command line options.
data/bin/fissher ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.dirname(__FILE__) + '/../lib'
4
+
5
+ require 'fissher_base'
6
+
7
+ fissher = FissherBase.new
8
+
9
+ fissher.go_time
@@ -0,0 +1,14 @@
1
+ {
2
+ "user": "user", // Default user name
3
+ "concurrency": "10", // Set to 0 to disable max concurrency
4
+ "default_gateway": "jumpbox1.sampledomain.com", // Default jump box, if needed.
5
+ "hostgroups": {
6
+ "app": {
7
+ "gateway": "jumpbox2.sampledomain.com", // Override the default
8
+ "hosts": ["appsrv01","appsrv02","appsrv03","appsrv04"]
9
+ },
10
+ "web": {
11
+ "hosts": ["websrv01","websrv02"]
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,53 @@
1
+
2
+ $:.unshift File.dirname(__FILE__)
3
+
4
+ require 'rubygems'
5
+ require 'net/ssh/multi'
6
+ require 'fissher_conf'
7
+ include FissherConf
8
+
9
+ class FissherBase
10
+ def go_time
11
+ opts = FissherConf.handle_opts unless !opts.nil?
12
+ abort "No hosts specified! Please use -H or -G!\n" unless !opts[:hostlist].nil?
13
+
14
+ Net::SSH::Multi.start(:concurrent_connections => opts[:concurrency]) do |session|
15
+ if opts[:gateway]
16
+ session.via opts[:gateway], opts[:user], :password => opts[:password]
17
+ end
18
+
19
+ # Create our connection list
20
+ opts[:hostlist].each do |host|
21
+ session.use host, :user => opts[:user], :password => opts[:password]
22
+ end
23
+
24
+ if opts[:command] =~ /^sudo/
25
+ if opts[:password].nil?
26
+ p = FissherConf::Misc.new
27
+ opts[:password] = p.getpass
28
+ end
29
+
30
+ # Get a PTY and exec command on servers
31
+ session.open_channel do |ch|
32
+ ch.request_pty do |c, success|
33
+ raise "could not request pty" unless success
34
+ ch.exec opts[:command]
35
+ ch.on_data do |c_, data|
36
+ if data =~ /\[sudo\]/
37
+ ch.send_data(opts[:password] + "\n")
38
+ else
39
+ puts "[#{ch[:host]}]: #{data}" unless data =~ /^\n$|^\s*$/
40
+ end
41
+ end
42
+ end
43
+ end
44
+ else
45
+ # Sudo isn't needed. We don't need a PTY.
46
+ session.exec(opts[:command])
47
+ end
48
+
49
+ # go time... for real.
50
+ session.loop
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,165 @@
1
+
2
+ #############################################
3
+ # Option/config parsing for Fissher #
4
+ #############################################
5
+
6
+ require 'json'
7
+ require 'highline/import'
8
+ require 'getopt/std'
9
+
10
+ module FissherConf
11
+ class Misc
12
+ def getpass(prompt="Enter remote password: ")
13
+ ask(prompt) {|q| q.echo = false}
14
+ end
15
+
16
+ # Method for returning hosts from hostgroup.
17
+ def group_hosts( grp,conf,conf_file )
18
+ if conf[:hostgroups][:"#{grp}"]
19
+ conf[:hostgroups][:"#{grp}"][:hosts]
20
+ else
21
+ abort "Fatal: hostgroup #{grp} not defined in #{conf_file}!\n"
22
+ end
23
+ end
24
+ end
25
+
26
+ def usage
27
+ puts "#{$0} [flags] [command]:\n"
28
+ puts "-G Hostgroup Execute command on all hosts listed in the JSON config for the\n"
29
+ puts " specified group.\n"
30
+ puts "-H Host1,Host2 Execute command on hosts listed on the command line\n"
31
+ puts "-g jumpbox Manually specify/override a jump server, if necessary.\n"
32
+ puts "-s Execute the provided commands with sudo."
33
+ puts "-u username Manually specify/override username to connect with.\n"
34
+ puts "-p Use password based authentication, specified via STDIN\n"
35
+ puts "-c config.json Manually specify the path to your fissher config file\n"
36
+ puts "-n num Number of concurrent connections. Enter 0 for unlimited.\n"
37
+ puts "-U username Specify an alternate user in conjunction with -s (E.G. -U webmaster)\n"
38
+ end
39
+
40
+ def die( msg )
41
+ puts "#{msg}\n"
42
+ usage
43
+ exit 1
44
+ end
45
+
46
+ def handle_opts
47
+ opt = Getopt::Std.getopts("pc:g:G:u:n:sH:hU:")
48
+ ret = Hash.new
49
+
50
+ if opt["h"]
51
+ usage
52
+ exit 1
53
+ end
54
+
55
+ # Import configuration, either from default or a custom JSON config
56
+ if opt["c"]
57
+ conf_file = opt["c"]
58
+ elsif File.exists?("#{Dir.home}/.fissherrc")
59
+ conf_file = "#{Dir.home}/.fissherrc"
60
+ elsif File.exists?("/etc/fissher/fissher.conf")
61
+ conf_file = "/etc/fissher/fissher.conf"
62
+ else
63
+ conf_file = File.dirname(__FILE__) + "/../etc/fissher.conf"
64
+ end
65
+
66
+ # Ensure the file exists
67
+ begin
68
+ config = JSON.parse(File.read(conf_file),:symbolize_names => true)
69
+ rescue
70
+
71
+ etcloc = File.dirname(__FILE__).to_s.gsub(/\/lib$/, '/etc')
72
+ puts <<EOB
73
+ **** It appears that you have not yet created your config file! ****
74
+ You can do this by copying the sample config to one of the following
75
+ locations:
76
+
77
+ ~/.fissherrc
78
+ /etc/fissher/fissher.conf
79
+ #{etcloc}/fissher.conf
80
+
81
+ You can find a copy of the sample in the following location:
82
+
83
+ #{etcloc}
84
+
85
+ A script will be included in the next release that will generate the file
86
+ for you if it does not already exist.
87
+ EOB
88
+ abort
89
+ end
90
+
91
+ # Use sudo for our command
92
+ if opt["s"]
93
+ if opt["U"]
94
+ sudo_cmd = "sudo -u #{opt['U']}"
95
+ else
96
+ sudo_cmd = "sudo"
97
+ end
98
+ end
99
+
100
+ # Gateway if an edgeserver is present
101
+ if opt["g"]
102
+ ret[:gateway] = opt["g"]
103
+ elsif opt["G"] && !config[:hostgroups][:"#{opt['G']}"][:gateway].nil?
104
+ ret[:gateway] = config[:hostgroups][:"#{opt['G']}"][:gateway]
105
+ elsif !config[:default_gateway].nil?
106
+ ret[:gateway] = config[:default_gateway]
107
+ end
108
+
109
+ # Hostgroup used for batch jobs
110
+ if opt["G"]
111
+ abort "You may only specify one of -G or -H!\n" unless opt["H"].nil?
112
+ h = Misc.new
113
+ ret[:hostlist] = h.group_hosts(opt["G"],config,conf_file)
114
+ end
115
+
116
+ # Username used by connections
117
+ if opt["u"]
118
+ ret[:user] = opt['u']
119
+ elsif config[:user]
120
+ ret[:user] = config[:user]
121
+ end
122
+
123
+ # Job Concurrency limit
124
+ if opt["n"] == '0'
125
+ ret[:concurrency] = nil
126
+ elsif opt["n"]
127
+ ret[:concurrency] = opt["n"].to_i
128
+ elsif config[:concurrency]
129
+ ret[:concurrency] = config[:concurrency].to_i
130
+ else
131
+ ret[:concurrency] = 10
132
+ end
133
+
134
+ # Host list
135
+ if opt["H"]
136
+ abort "You may only specify one of -G or -H!\n" unless opt["G"].nil?
137
+ if opt["H"] =~ /,/
138
+ ret[:hostlist] = opt["H"].split(",")
139
+ else
140
+ ret[:hostlist] = [ opt["H"] ]
141
+ end
142
+ end
143
+
144
+ # Get our account password
145
+ if opt["p"]
146
+ p = Misc.new
147
+ ret[:password] = p.getpass()
148
+ else
149
+ ret[:password] = nil
150
+ end
151
+
152
+ # Our command
153
+ if ARGV.count >= 1
154
+ if sudo_cmd
155
+ ret[:command] = "#{sudo_cmd} " + ARGV.join(' ').to_s
156
+ else
157
+ ret[:command] = ARGV.join(' ').to_s
158
+ end
159
+ else
160
+ die "No command specified!\n" unless !ret[:command].nil?
161
+ end
162
+ ret
163
+ end
164
+ end
165
+
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fissher
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jeff Hagadorn
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: net-ssh-multi
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: getopt
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: json
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: highline
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: A utility written to perform batch commands on many servers over SSH.
79
+ Supports jump servers and hostgroups via a JSON config file.
80
+ email: jeff@aletheia.io
81
+ executables:
82
+ - fissher
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - lib/fissher_conf.rb
87
+ - lib/fissher_base.rb
88
+ - etc/fissher.conf.sample
89
+ - README.rdoc
90
+ - bin/fissher
91
+ homepage: http://github.com/dahui/fissher
92
+ licenses: []
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 1.8.25
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Fissher
115
+ test_files: []