ccsh 0.0.3
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.
- checksums.yaml +7 -0
- data/bin/ccsh +5 -0
- data/lib/ccsh.rb +78 -0
- data/lib/ccsh/host.rb +45 -0
- data/lib/ccsh/hosts.rb +66 -0
- data/lib/ccsh/options.rb +57 -0
- data/lib/ccsh/ssh.rb +78 -0
- data/lib/ccsh/utils.rb +70 -0
- data/lib/ccsh/version.rb +8 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e18fe57923e83f6f801b3c17cffab2d0928df142
|
4
|
+
data.tar.gz: 8fa3fb86f5626fa09a4f657379626bcc1e4c4d72
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a5b6525d2f5d690e88d2cb9322a42400d5909e625d797d24b7a7c64bf31607c7026bc34de478936bae625ec89dee37b936b8d1075e35b17ab21b5f8c9f72fede
|
7
|
+
data.tar.gz: f40ee7dcf33c3071a3ff83c5b105ac66ae3b07cfc2d79109aecc96d864331d4dd389daa932c74faecb90b5a21440989b603f6e004cb169c44b4c0a779c947b10
|
data/bin/ccsh
ADDED
data/lib/ccsh.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'rubygems' unless defined?(Gem)
|
2
|
+
|
3
|
+
module CCSH
|
4
|
+
require 'ccsh/version.rb'
|
5
|
+
require 'ccsh/utils.rb'
|
6
|
+
require 'ccsh/options.rb'
|
7
|
+
require 'ccsh/host'
|
8
|
+
require 'ccsh/hosts.rb'
|
9
|
+
require 'ccsh/ssh.rb'
|
10
|
+
|
11
|
+
def self.execute!
|
12
|
+
options = CCSH::Options.parse_options ARGV
|
13
|
+
|
14
|
+
ENV['CCSH_DEBUG'] = "true" if options[:debug]
|
15
|
+
ENV['CCSH_VERBOSE'] = "true" if options[:verbose]
|
16
|
+
|
17
|
+
raise "You must to specified a hostname or group" if options[:targets].empty?
|
18
|
+
|
19
|
+
filename = options[:hosts]
|
20
|
+
targets = options[:targets]
|
21
|
+
|
22
|
+
self.start_cli CCSH::Hosts.new.parser!(filename).filter_by(targets)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.with_info
|
26
|
+
cmd_start = Time.now
|
27
|
+
cmd = yield
|
28
|
+
cmd_end = Time.now
|
29
|
+
|
30
|
+
info = {
|
31
|
+
'rc' => cmd.return_code,
|
32
|
+
'time' => cmd_end - cmd_start,
|
33
|
+
}.inspect
|
34
|
+
|
35
|
+
puts ">>> #{cmd.hostname} #{info}"
|
36
|
+
STDERR.puts cmd.stderr if cmd.stderr != nil && cmd.stderr != ''
|
37
|
+
puts cmd.stdout if cmd.stdout != nil && cmd.stdout != ''
|
38
|
+
puts
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.start_cli(hosts)
|
42
|
+
quit = false
|
43
|
+
loop do
|
44
|
+
begin
|
45
|
+
CCSH::Utils.handle_signals
|
46
|
+
printf "ccsh> "
|
47
|
+
command = STDIN.gets.chomp
|
48
|
+
|
49
|
+
if command != ''
|
50
|
+
CCSH::Utils.exit_console 0 if command == 'quit'
|
51
|
+
CCSH::Utils.exit_console 0 if command == 'exit'
|
52
|
+
|
53
|
+
if command == 'clear'
|
54
|
+
CCSH::Utils.clear_console
|
55
|
+
elsif command == 'reset'
|
56
|
+
CCSH::Utils.reset_console
|
57
|
+
else
|
58
|
+
threads = []
|
59
|
+
hosts.each do |host|
|
60
|
+
threads << Thread.new do
|
61
|
+
|
62
|
+
with_info do
|
63
|
+
host.run command
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
threads.each(&:join)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
rescue Exception => exception
|
72
|
+
STDERR.puts exception.message
|
73
|
+
STDERR.puts
|
74
|
+
raise exception
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/ccsh/host.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module CCSH
|
2
|
+
class Host
|
3
|
+
attr_accessor :name
|
4
|
+
attr_accessor :port
|
5
|
+
attr_accessor :user
|
6
|
+
attr_accessor :groups
|
7
|
+
attr_accessor :hostname
|
8
|
+
attr_accessor :password
|
9
|
+
attr_accessor :private_key
|
10
|
+
attr_accessor :ssh_options
|
11
|
+
attr_accessor :timeout
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@name = ''
|
15
|
+
@port = 22
|
16
|
+
@user = 'root'
|
17
|
+
@groups = ''
|
18
|
+
@hostname = '127.0.0.1'
|
19
|
+
@password = nil
|
20
|
+
@private_key = nil
|
21
|
+
@ssh_options = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def run(command)
|
25
|
+
CCSH::Utils.verbose("Running command '#{@command}' on #{@hostname}")
|
26
|
+
command = CCSH::SSH.start do |ssh|
|
27
|
+
ssh.user = @user
|
28
|
+
ssh.port = @port
|
29
|
+
ssh.options = @ssh_options
|
30
|
+
ssh.password = @password
|
31
|
+
ssh.private_key = @private_key
|
32
|
+
ssh.options = @ssh_options
|
33
|
+
|
34
|
+
ssh.command = command
|
35
|
+
ssh.hostname = @hostname || @name
|
36
|
+
end
|
37
|
+
|
38
|
+
return command
|
39
|
+
end
|
40
|
+
|
41
|
+
def contain?(target)
|
42
|
+
return @groups.include? target
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/ccsh/hosts.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
module CCSH
|
2
|
+
require 'yaml'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
require 'ccsh/host'
|
6
|
+
|
7
|
+
class Hosts
|
8
|
+
attr_accessor :defaults
|
9
|
+
attr_accessor :hosts
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
# TODO
|
13
|
+
end
|
14
|
+
|
15
|
+
def parser!(filename)
|
16
|
+
begin
|
17
|
+
file = Pathname.new(filename)
|
18
|
+
|
19
|
+
raise "The file does not exists: '#{file.realpath}'" if not file.exist?
|
20
|
+
raise "The host file is not regular file: '#{file.realpath}'" if not file.file?
|
21
|
+
|
22
|
+
hostfile = YAML.load_file(file.realpath)
|
23
|
+
|
24
|
+
@hosts = []
|
25
|
+
@defaults = CCSH::Utils.merge_defaults(hostfile['defaults'])
|
26
|
+
|
27
|
+
hostfile['hosts'].each do |h|
|
28
|
+
host = Host.new
|
29
|
+
|
30
|
+
host.name = CCSH::Utils.get_options('name', h, @defaults['name'])
|
31
|
+
host.user = CCSH::Utils.get_options('user', h, @defaults['user'])
|
32
|
+
host.port = CCSH::Utils.get_options('port', h, @defaults['port'])
|
33
|
+
|
34
|
+
host.hostname = CCSH::Utils.get_options('hostname', h, @defaults['hostname'])
|
35
|
+
host.password = CCSH::Utils.get_options('password', h, @defaults['password'])
|
36
|
+
host.private_key = CCSH::Utils.get_options('private_key', h, @defaults['private_key'])
|
37
|
+
host.ssh_options = CCSH::Utils.get_options('ssh_options', h, @defaults['ssh_options'])
|
38
|
+
host.timeout = CCSH::Utils.get_options('timeout', h, @defaults['timeout'])
|
39
|
+
|
40
|
+
host.groups = h['groups']
|
41
|
+
|
42
|
+
@hosts << host
|
43
|
+
end
|
44
|
+
rescue Exception => e
|
45
|
+
puts e.message
|
46
|
+
puts e.backtrace
|
47
|
+
puts
|
48
|
+
end
|
49
|
+
|
50
|
+
return self
|
51
|
+
end
|
52
|
+
|
53
|
+
def filter_by(targets)
|
54
|
+
return @hosts if targets.include? 'all'
|
55
|
+
|
56
|
+
hosts = []
|
57
|
+
@hosts.each do |host|
|
58
|
+
targets.each do |target|
|
59
|
+
hosts << host if host.groups.include? target
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
return hosts
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/ccsh/options.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module CCSH
|
2
|
+
module Options
|
3
|
+
require 'ostruct'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
def self.parse_options(args)
|
7
|
+
options = OpenStruct.new
|
8
|
+
|
9
|
+
# default values
|
10
|
+
user_home = File.expand_path('~')
|
11
|
+
|
12
|
+
options.config = "#{user_home}/.ccsh/config.yaml"
|
13
|
+
options.hosts = "#{user_home}/.ccsh/hosts.yaml"
|
14
|
+
options.output = ""
|
15
|
+
options.debug = false
|
16
|
+
options.verbose = false
|
17
|
+
options.check = false
|
18
|
+
|
19
|
+
# open parser
|
20
|
+
opt_parser = OptionParser.new do |opts|
|
21
|
+
opts.banner = "Usage: ccsh [options] GROUP1 GROUP2 ... "
|
22
|
+
opts.separator ""
|
23
|
+
opts.separator "Options: "
|
24
|
+
|
25
|
+
opts.on("-c", "--config CONFIG_FILE", "Configuration file (default: ~/.ccsh/config.yaml)") do |cfg|
|
26
|
+
options.config = cfg
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("-d", "--[no-]debug", "Run Debug mode") do |d|
|
30
|
+
options.debug = d
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("-h", "--hosts HOST_FILE", "Specified hosts file") do |h|
|
34
|
+
options.hosts = h
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("-k", "--[no-]check", "Check host connection before continuing") do |k|
|
38
|
+
options.check = k
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
42
|
+
options.verbose = v
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on("--version", "Display version") do |v|
|
46
|
+
build = "build #{CCSH::BUILD_NUMBER}" if CCSH::BUILD_NUMBER != nil
|
47
|
+
puts "CCSH version #{CCSH::VERSION} #{build}"
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
end
|
51
|
+
opt_parser.parse!(args)
|
52
|
+
|
53
|
+
options.targets = args
|
54
|
+
return options
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/ccsh/ssh.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
module CCSH
|
2
|
+
module SSH
|
3
|
+
require 'net/ssh'
|
4
|
+
|
5
|
+
def self.start
|
6
|
+
cmd = RemoteCommand.new
|
7
|
+
yield cmd
|
8
|
+
|
9
|
+
raise 'Could not parser the remote command' if cmd.command == nil
|
10
|
+
raise 'Could not parser the hostname' if cmd.hostname == nil
|
11
|
+
|
12
|
+
return cmd.execute!
|
13
|
+
end
|
14
|
+
|
15
|
+
class RemoteCommand
|
16
|
+
attr_accessor :command
|
17
|
+
attr_accessor :hostname
|
18
|
+
attr_accessor :options
|
19
|
+
attr_accessor :user
|
20
|
+
attr_accessor :port
|
21
|
+
attr_accessor :password
|
22
|
+
attr_accessor :private_key
|
23
|
+
attr_accessor :options
|
24
|
+
|
25
|
+
attr_accessor :stdout
|
26
|
+
attr_accessor :stderr
|
27
|
+
attr_accessor :return_code
|
28
|
+
attr_accessor :return_signal
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
@command = nil
|
32
|
+
@hostname = nil
|
33
|
+
@options = []
|
34
|
+
@stdout = ''
|
35
|
+
@stderr = ''
|
36
|
+
end
|
37
|
+
|
38
|
+
def execute!
|
39
|
+
@options = {
|
40
|
+
:password => @password,
|
41
|
+
:keys => [@private_key],
|
42
|
+
:port => @port,
|
43
|
+
:host_key => @options['host_key'],
|
44
|
+
:timeout => @options['timeout'],
|
45
|
+
}.select {|key,value| value != nil}
|
46
|
+
|
47
|
+
raise "something" unless CCSH::Utils.valid_ssh(@options)
|
48
|
+
|
49
|
+
Net::SSH.start(@hostname, @user, @options) do |ssh|
|
50
|
+
ssh.open_channel do |ch|
|
51
|
+
ch.exec @command do |ch, success|
|
52
|
+
raise "Could execute command #{command} on #{host}" unless success
|
53
|
+
end
|
54
|
+
|
55
|
+
ch.on_data do |c, data|
|
56
|
+
@stdout << data
|
57
|
+
end
|
58
|
+
|
59
|
+
ch.on_extended_data do |c, type, data|
|
60
|
+
@stderr << data
|
61
|
+
end
|
62
|
+
|
63
|
+
ch.on_request("exit-status") do |c,data|
|
64
|
+
@return_code = data.read_long
|
65
|
+
end
|
66
|
+
|
67
|
+
ch.on_request("exit-signal") do |c, data|
|
68
|
+
@return_signal = data.read_long
|
69
|
+
end
|
70
|
+
|
71
|
+
end.wait
|
72
|
+
end
|
73
|
+
|
74
|
+
return self
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/ccsh/utils.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module CCSH
|
2
|
+
module Utils
|
3
|
+
def self.merge_defaults(defaults)
|
4
|
+
defaultsValues = {
|
5
|
+
'user' => 'root',
|
6
|
+
'port' => '22',
|
7
|
+
'private_key' => '~/.ssh/id_rsa',
|
8
|
+
}
|
9
|
+
defaultsValues.merge!(defaults) if defaults != nil
|
10
|
+
|
11
|
+
ssh_options = {
|
12
|
+
'timeout' => 720,
|
13
|
+
'ssh-rsa' => 'ssh-rsa',
|
14
|
+
}
|
15
|
+
|
16
|
+
if defaults['ssh_options'] != nil
|
17
|
+
defaultsValues['ssh_options'] = ssh_options.merge!(defaults['ssh_options'])
|
18
|
+
else
|
19
|
+
defaultsValues['ssh_options'] = ssh_options
|
20
|
+
end
|
21
|
+
|
22
|
+
return defaultsValues
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.get_options(item, object, default)
|
26
|
+
if object.key? item
|
27
|
+
return object[item]
|
28
|
+
end
|
29
|
+
|
30
|
+
return default
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.valid_ssh(options)
|
34
|
+
return true
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.debug(msg)
|
38
|
+
puts "DEBUG", msg if ENV['CCSH_DEBUG'] == "true"
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.verbose(msg)
|
42
|
+
puts msg if ENV['CCSH_VERBOSE'] == "true"
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.handle_signals
|
46
|
+
Signal.trap('INT') do
|
47
|
+
self.exit_console 0
|
48
|
+
end
|
49
|
+
|
50
|
+
Signal.trap('TERM') do
|
51
|
+
self.exit_console 0
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.exit_console(code, msg = nil)
|
56
|
+
puts msg if msg != nil
|
57
|
+
|
58
|
+
puts "\nBye..."
|
59
|
+
exit code || 0
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.clear_console
|
63
|
+
printf "\e[H\e[2J"
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.clear_console
|
67
|
+
printf("\033c");
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/ccsh/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ccsh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rafael Silva <raffs>
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: net-ssh
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.16'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.16'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
description: Interactive shell console, connected into multiple clients
|
70
|
+
email:
|
71
|
+
- rafaeloliveira.cs@gmail.com
|
72
|
+
executables:
|
73
|
+
- ccsh
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- bin/ccsh
|
78
|
+
- lib/ccsh.rb
|
79
|
+
- lib/ccsh/host.rb
|
80
|
+
- lib/ccsh/hosts.rb
|
81
|
+
- lib/ccsh/options.rb
|
82
|
+
- lib/ccsh/ssh.rb
|
83
|
+
- lib/ccsh/utils.rb
|
84
|
+
- lib/ccsh/version.rb
|
85
|
+
homepage: https://github.com/raffs/ccsh
|
86
|
+
licenses:
|
87
|
+
- Apache-2.0
|
88
|
+
metadata: {}
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '2.0'
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
requirements: []
|
104
|
+
rubyforge_project:
|
105
|
+
rubygems_version: 2.6.14
|
106
|
+
signing_key:
|
107
|
+
specification_version: 4
|
108
|
+
summary: Interactive multiple client shell command
|
109
|
+
test_files: []
|