jssh 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/jssh +25 -105
- data/lib/jssh.rb +71 -2
- data/lib/jssh/printer.rb +45 -0
- data/lib/jssh/string.rb +23 -0
- data/lib/jssh/version.rb +2 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd8ac63db606331d0c105e6e284e684a31634683
|
4
|
+
data.tar.gz: e4033249bf64dd46edf79457b33397e1a9039b40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 593a5f5473b552ea2e9e350abbbf4a6b84570fa9f6de8cd540ced58c5718a76218078cfd574b03bac1758645f992e5ecab2569cfbba476ea597c19acb211d042
|
7
|
+
data.tar.gz: bff5756a0b8b6431585bd63a95bc9c9788f7d6193ed254a2b513c23de80597ade2882e3a5adb8de7168035b1d07cf651e32f3f3475a60868e1fb1e4dcf9b27f2
|
data/bin/jssh
CHANGED
@@ -2,42 +2,10 @@
|
|
2
2
|
require 'net/ssh'
|
3
3
|
require 'optparse'
|
4
4
|
require 'yaml'
|
5
|
-
|
6
|
-
class String
|
7
|
-
def to_black
|
8
|
-
"\033[30m#{self}\033[0m"
|
9
|
-
end
|
10
|
-
def to_red
|
11
|
-
"\033[31m#{self}\033[0m"
|
12
|
-
end
|
13
|
-
def to_yellow
|
14
|
-
"\033[33m#{self}\033[0m"
|
15
|
-
end
|
16
|
-
def to_blue
|
17
|
-
"\033[34m#{self}\033[0m"
|
18
|
-
end
|
19
|
-
def to_white
|
20
|
-
"\033[37m#{self}\033[0m"
|
21
|
-
end
|
22
|
-
def to_green
|
23
|
-
"\033[32m#{self}\033[0m"
|
24
|
-
end
|
25
|
-
def to_cyan
|
26
|
-
"\033[36m#{self}\033[0m"
|
27
|
-
end
|
28
|
-
def to_magenta
|
29
|
-
"\033[35m#{self}\033[0m"
|
30
|
-
end
|
31
|
-
def to_bold
|
32
|
-
"\033[1m#{self}\033[0m"
|
33
|
-
end
|
34
|
-
def to_underline
|
35
|
-
"\033[4m#{self}\033[0m"
|
36
|
-
end
|
37
|
-
end
|
5
|
+
require "jssh"
|
38
6
|
|
39
7
|
options = {}
|
40
|
-
OptionParser.new do |opts|
|
8
|
+
op=OptionParser.new do |opts|
|
41
9
|
opts.banner = "Usage: jssh [options]"
|
42
10
|
opts.on("-f file","--hostfile file", "host file, every host occupy a line") do |v|
|
43
11
|
options[:hostfile] = v
|
@@ -61,6 +29,9 @@ OptionParser.new do |opts|
|
|
61
29
|
opts.on("-b","--break", "pause after first operation") do |v|
|
62
30
|
options[:pause] = v
|
63
31
|
end
|
32
|
+
opts.on("-g gsize","--groupsize gsize",OptionParser::OctalInteger, "The size of parallel group, default is 10") do |v|
|
33
|
+
options[:gsize] = v
|
34
|
+
end
|
64
35
|
opts.on("-c cmdfile","--cmdfile cmdfile", "script path") do |v|
|
65
36
|
options[:cmdfile] = v
|
66
37
|
unless File.exists? v
|
@@ -68,6 +39,9 @@ OptionParser.new do |opts|
|
|
68
39
|
exit
|
69
40
|
end
|
70
41
|
end
|
42
|
+
opts.on("-o outputfile","--output outputfile", "output file") do |v|
|
43
|
+
options[:output] = v
|
44
|
+
end
|
71
45
|
opts.on("--help", "help") do |v|
|
72
46
|
puts opts
|
73
47
|
exit
|
@@ -75,7 +49,12 @@ OptionParser.new do |opts|
|
|
75
49
|
opts.on("--debug", "debug inputs") do |v|
|
76
50
|
options[:debug]=v
|
77
51
|
end
|
78
|
-
end
|
52
|
+
end
|
53
|
+
begin op.parse!
|
54
|
+
rescue => e
|
55
|
+
puts e.to_s.to_red
|
56
|
+
exit
|
57
|
+
end
|
79
58
|
|
80
59
|
# If neither the rsa file or password specified, go get rsa file in home directory
|
81
60
|
unless options[:key] || options[:password]
|
@@ -100,75 +79,9 @@ if options[:debug]
|
|
100
79
|
exit
|
101
80
|
end
|
102
81
|
|
103
|
-
lambda{puts "No command or command file specify";exit}.call unless options[:cmdfile] || options[:command]
|
104
|
-
lambda{puts "Please specify the user";exit}.call unless options[:user]
|
105
|
-
lambda{puts "Please specify the rsa file or password";exit}.call unless options[:key] || options[:password]
|
106
|
-
|
107
|
-
class Jssh
|
108
|
-
attr_accessor :hosts,:user, :auth_cfg, :cmd, :printer
|
109
|
-
|
110
|
-
def initialize
|
111
|
-
@auth_cfg={}
|
112
|
-
self.printer=:on
|
113
|
-
@queue=Queue.new
|
114
|
-
end
|
115
|
-
def messages
|
116
|
-
@queue
|
117
|
-
end
|
118
|
-
|
119
|
-
def go(from=0,len=nil)
|
120
|
-
len||=self.hosts.size
|
121
|
-
to=from+len-1
|
122
|
-
password=self.auth_cfg[:password]
|
123
|
-
keys=[self.auth_cfg[:key]].reject{|x| x.nil?}
|
124
|
-
self.hosts[from..to].each_with_index do |host,i|
|
125
|
-
result=""
|
126
|
-
begin
|
127
|
-
Net::SSH.start(host,self.user,:keys=>keys,:password=>password) do |ssh|
|
128
|
-
result=ssh.exec!(self.cmd)
|
129
|
-
end
|
130
|
-
rescue=>e
|
131
|
-
result=e.to_s
|
132
|
-
end
|
133
|
-
yield host,result if block_given?
|
134
|
-
end
|
135
|
-
end
|
136
|
-
def execute(from=0,len=nil,group_size=10)
|
137
|
-
pr=start_printer(self)
|
138
|
-
threads=[]
|
139
|
-
len||=self.hosts.size
|
140
|
-
to=from+len-1
|
141
|
-
divider(self.hosts[from..to].size,group_size) do |df,dt|
|
142
|
-
threads<<Thread.new(self,from+df,from+dt) do |rssh,f,t|
|
143
|
-
rssh.go(f,t) do |h,r|
|
144
|
-
output=("="*20+h+"="*20).to_yellow+"\n"+r
|
145
|
-
rssh.messages.push output
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
threads.each{|t| t.join}
|
150
|
-
loop{ break if self.messages.size==0 } if self.printer
|
151
|
-
Thread.kill pr
|
152
|
-
end
|
153
|
-
|
154
|
-
private
|
155
|
-
def start_printer(jssh)
|
156
|
-
Thread.new(jssh) do |rssh|
|
157
|
-
while true
|
158
|
-
puts rssh.messages.pop if rssh.printer
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
def divider(total,gsize)
|
163
|
-
gcnt=total/gsize
|
164
|
-
gcnt=gcnt+1 if total%gsize!=0
|
165
|
-
gcnt.times do |i|
|
166
|
-
from,len=i*gsize,gsize
|
167
|
-
len=total%gsize if i==gcnt-1
|
168
|
-
yield from,len
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
82
|
+
lambda{puts "No command or command file specify".to_red;exit}.call unless options[:cmdfile] || options[:command]
|
83
|
+
lambda{puts "Please specify the user".to_red;exit}.call unless options[:user]
|
84
|
+
lambda{puts "Please specify the rsa file or password".to_red;exit}.call unless options[:key] || options[:password]
|
172
85
|
|
173
86
|
rs=Jssh.new
|
174
87
|
if options[:hostfile]
|
@@ -176,6 +89,9 @@ if options[:hostfile]
|
|
176
89
|
else
|
177
90
|
orig_hosts=options[:hostlist].split(",")
|
178
91
|
end
|
92
|
+
|
93
|
+
rs.printer=Printer.new(FileExecutor.new(options[:output])) if options[:output]
|
94
|
+
|
179
95
|
rs.user=options[:user]
|
180
96
|
rs.auth_cfg[:password]=options[:password]
|
181
97
|
rs.auth_cfg[:key]=options[:key]
|
@@ -190,6 +106,7 @@ unless options[:pause]
|
|
190
106
|
puts "Finished!".to_green
|
191
107
|
exit
|
192
108
|
end
|
109
|
+
|
193
110
|
rs.hosts=orig_hosts.take 1
|
194
111
|
rs.execute
|
195
112
|
puts "Now the operations on the first host is completed, we're going to it for a check.".to_yellow
|
@@ -212,7 +129,10 @@ print "Continue to finish all the left operations by parallel(p) or serial(s), [
|
|
212
129
|
ans=$stdin.gets.chomp
|
213
130
|
rs.hosts=orig_hosts[1..-1]
|
214
131
|
if ans.downcase=='p'
|
215
|
-
|
132
|
+
options[:gsize]= rs.hosts.size/100 if options[:gsize].nil? || rs.hosts.size/options[:gsize]>100 || options[:gsize]<1
|
133
|
+
gs=options[:gsize]==0 ? 1 : options[:gsize]
|
134
|
+
rs.printer.instant=false
|
135
|
+
rs.execute 0,nil,gs
|
216
136
|
else
|
217
137
|
rs.execute 0,nil,rs.hosts.size
|
218
138
|
end
|
data/lib/jssh.rb
CHANGED
@@ -1,5 +1,74 @@
|
|
1
1
|
require "jssh/version"
|
2
|
+
require "jssh/printer"
|
3
|
+
require 'jssh/string'
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
+
class Jssh
|
6
|
+
attr_accessor :hosts,:user, :auth_cfg, :cmd, :printer
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@auth_cfg={}
|
10
|
+
self.printer=Printer.new(StdoutExecutor.new)
|
11
|
+
@queue=Queue.new
|
12
|
+
end
|
13
|
+
def messages
|
14
|
+
@queue
|
15
|
+
end
|
16
|
+
|
17
|
+
def go(from=0,len=nil)
|
18
|
+
len||=self.hosts.size
|
19
|
+
to=from+len-1
|
20
|
+
password=self.auth_cfg[:password]
|
21
|
+
keys=[self.auth_cfg[:key]].reject{|x| x.nil?}
|
22
|
+
self.hosts[from..to].each_with_index do |host,i|
|
23
|
+
begin
|
24
|
+
Net::SSH.start(host,self.user,:keys=>keys,:password=>password) do |ssh|
|
25
|
+
result=ssh.exec!(self.cmd) || ''
|
26
|
+
ssh.exec!(self.cmd) do |ch,stream,data|
|
27
|
+
data||=''
|
28
|
+
yield host,data,false if block_given?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
rescue=>e
|
32
|
+
result=e.to_s
|
33
|
+
end
|
34
|
+
result||=''
|
35
|
+
yield host,result,true if block_given?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
def execute(from=0,len=nil,group_size=1)
|
39
|
+
pr=start_printer(self)
|
40
|
+
threads=[]
|
41
|
+
len||=self.hosts.size
|
42
|
+
to=from+len-1
|
43
|
+
divider(self.hosts[from..to].size,group_size) do |df,dt|
|
44
|
+
threads<<Thread.new(self,from+df,from+dt) do |rssh,f,t|
|
45
|
+
rssh.go(f,t) do |h,r,over|
|
46
|
+
rssh.messages.push({'host'=>h,'content'=>r,'finished'=>over})
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
threads.each{|t| t.join}
|
51
|
+
loop{ break if self.printer.finished? }
|
52
|
+
Thread.kill pr
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def start_printer(jssh)
|
57
|
+
jssh.printer.submit_jobs(jssh.hosts)
|
58
|
+
Thread.new(jssh) do |rssh|
|
59
|
+
while true
|
60
|
+
data=rssh.messages.pop
|
61
|
+
rssh.printer.print data['host'],data['content'],data['finished']
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
def divider(total,gsize)
|
66
|
+
gcnt=total/gsize
|
67
|
+
gcnt=gcnt+1 if total%gsize!=0
|
68
|
+
gcnt.times do |i|
|
69
|
+
from,len=i*gsize,gsize
|
70
|
+
len=total%gsize if i==gcnt-1 && total%gsize!=0
|
71
|
+
yield from,len
|
72
|
+
end
|
73
|
+
end
|
5
74
|
end
|
data/lib/jssh/printer.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
class Printer
|
2
|
+
attr_accessor :instant
|
3
|
+
def initialize(executor)
|
4
|
+
self.instant=true
|
5
|
+
@executor=executor
|
6
|
+
end
|
7
|
+
def submit_jobs(hosts)
|
8
|
+
@memo={}
|
9
|
+
hosts.each{|j| @memo[j]=nil }
|
10
|
+
end
|
11
|
+
def print(host,content,is_end)
|
12
|
+
if self.instant
|
13
|
+
lambda{ @executor.puts ("="*20+host+"="*20).to_yellow; @memo[host]='' }.call unless @memo[host]
|
14
|
+
@executor.puts content if content
|
15
|
+
@memo.delete host if is_end
|
16
|
+
else
|
17
|
+
@memo[host]=("="*20+host+"="*20).to_yellow+"\n" unless @memo[host]
|
18
|
+
@memo[host] << content if content
|
19
|
+
lambda{ @executor.puts @memo[host]; @memo.delete host }.call if is_end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
def finished?
|
23
|
+
@memo.empty?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class StdoutExecutor
|
28
|
+
def puts(str)
|
29
|
+
$stdout.puts str
|
30
|
+
end
|
31
|
+
end
|
32
|
+
class FileExecutor
|
33
|
+
def initialize(filename)
|
34
|
+
@file=File.open(filename,'a')
|
35
|
+
end
|
36
|
+
def puts(str)
|
37
|
+
@file.puts str
|
38
|
+
end
|
39
|
+
end
|
40
|
+
class NullExecutor
|
41
|
+
def puts(str)
|
42
|
+
#Do nothing
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
data/lib/jssh/string.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
class String
|
2
|
+
def to_black
|
3
|
+
"\033[30m#{self}\033[0m"
|
4
|
+
end
|
5
|
+
def to_red
|
6
|
+
"\033[31m#{self}\033[0m"
|
7
|
+
end
|
8
|
+
def to_yellow
|
9
|
+
"\033[33m#{self}\033[0m"
|
10
|
+
end
|
11
|
+
def to_blue
|
12
|
+
"\033[34m#{self}\033[0m"
|
13
|
+
end
|
14
|
+
def to_white
|
15
|
+
"\033[37m#{self}\033[0m"
|
16
|
+
end
|
17
|
+
def to_green
|
18
|
+
"\033[32m#{self}\033[0m"
|
19
|
+
end
|
20
|
+
def to_cyan
|
21
|
+
"\033[36m#{self}\033[0m"
|
22
|
+
end
|
23
|
+
end
|
data/lib/jssh/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "0.0.
|
1
|
+
class Jssh
|
2
|
+
VERSION = "0.0.3"
|
3
3
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jssh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- qjpcpu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -68,6 +68,8 @@ files:
|
|
68
68
|
- bin/jssh
|
69
69
|
- jssh.gemspec
|
70
70
|
- lib/jssh.rb
|
71
|
+
- lib/jssh/printer.rb
|
72
|
+
- lib/jssh/string.rb
|
71
73
|
- lib/jssh/version.rb
|
72
74
|
homepage: https://github.com/qjpcpu/jssh
|
73
75
|
licenses:
|