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.
@@ -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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'CCSH'
4
+
5
+ CCSH.execute!
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,8 @@
1
+ module CCSH
2
+
3
+ # ccsh relased version
4
+ VERSION = "0.0.3"
5
+
6
+ # the git commit tag number that generate this build
7
+ BUILD_NUMBER = "dev-#{VERSION}"
8
+ end
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: []