expectacle 0.0.1 → 0.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.
- checksums.yaml +4 -4
- data/.yardopts +2 -0
- data/Gemfile +6 -0
- data/README.md +50 -1
- data/Rakefile +12 -0
- data/exe/run_command +10 -1
- data/lib/expectacle/thrower.rb +24 -18
- data/lib/expectacle/thrower_base.rb +38 -15
- data/lib/expectacle/version.rb +2 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 70d0dd97af8fb79becf1a01a31b11fc81ddc3c64
|
4
|
+
data.tar.gz: 20bf4dd2c8fca9af93f07bbf90b011481169b88b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2bb97f3b5bce0bcdf449b1ab9350cd403099e6d5e2de11356b05159ec872fcff2681142311e00830ceffb44b91ec59a396787f06728022cf3744fd906860e56
|
7
|
+
data.tar.gz: 94941d88e8c6ab26e3c940fd3af4ef5ecd112b710ebe117103f040f14d5476ccdd412520124d964a1aca1e2d7dbbda5c88ab88213b970875546ec5203140cf25
|
data/.yardopts
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -55,6 +55,55 @@ Input: (type password)
|
|
55
55
|
- it is a list of commands.
|
56
56
|
- Each files are written by YAML.
|
57
57
|
|
58
|
+
### Parameter expansion and preview
|
59
|
+
Expectacle has parameter expansion feature using ERB.
|
60
|
+
In a command list file,
|
61
|
+
you can write command strings including environment variable and parameters defined in host file.
|
62
|
+
See [Parameter definitions](#parameter-definitions) section about details of parameter expansion feature.
|
63
|
+
|
64
|
+
Thereby, there are some risks sending danger commands by using wrong parameter and command definitions.
|
65
|
+
|
66
|
+
Then, you can preview expanded command strings to send a host and parameters before execute actually.
|
67
|
+
For Example:
|
68
|
+
```
|
69
|
+
stereocat@tftpserver:~/expectacle$ bundle exec run_command -p -h l2switch.yml -c cisco_save_config_tftp.yml
|
70
|
+
---
|
71
|
+
:spawn_cmd: ssh -o StrictHostKeyChecking=no -o KexAlgorithms=+diffie-hellman-group1-sha1
|
72
|
+
-l cisco 192.168.20.150
|
73
|
+
:prompt:
|
74
|
+
:password: "^Password\\s*:"
|
75
|
+
:username: "^Username\\s*:"
|
76
|
+
:sub1: "\\][:\\?]"
|
77
|
+
:sub2: "\\[confirm\\]"
|
78
|
+
:yn: "\\[yes\\/no\\]:"
|
79
|
+
:command1: "^[\\w\\-]+>"
|
80
|
+
:command2: "^[\\w\\-]+(:?\\(config\\))?\\#"
|
81
|
+
:enable_password: SAME_AS_LOGIN_PASSWORD
|
82
|
+
:enable_command: enable
|
83
|
+
:host:
|
84
|
+
:hostname: l2sw1
|
85
|
+
:type: c3750g
|
86
|
+
:ipaddr: 192.168.20.150
|
87
|
+
:protocol: ssh
|
88
|
+
:username: cisco
|
89
|
+
:password: ********
|
90
|
+
:enable: ********
|
91
|
+
:tftp_server: 192.168.20.170
|
92
|
+
:commands:
|
93
|
+
- copy run start
|
94
|
+
- copy run tftp://192.168.20.170/l2sw1.confg
|
95
|
+
---
|
96
|
+
(snip)
|
97
|
+
```
|
98
|
+
**Notice** : Passwords were masked above example, but actually, raw password string are printed out.
|
99
|
+
|
100
|
+
### Use Syslog
|
101
|
+
|
102
|
+
With `-s`/`--syslog`, [run_command](./exe/run_command) changes logging instance to `syslog/logger`.
|
103
|
+
So, log messages are printed out to syslog on localhost.
|
104
|
+
|
105
|
+
$ bundle exec run_command -rs -h l2switch.yml -c cisco_show_arp.yml
|
106
|
+
|
58
107
|
## Parameter Definitions
|
59
108
|
|
60
109
|
### Expectacle::Thrower
|
@@ -147,7 +196,7 @@ Feature for sub-prompt (interactive command) is not enough.
|
|
147
196
|
Now, Expectacle sends fixed command for sub-prompt.
|
148
197
|
(These actions were defined for cisco to execute above "copy run" example...)
|
149
198
|
- Yex/No (`:yn`) : always sends "yes"
|
150
|
-
- Sub prompt (`:sub1` and `:sub2`) : Empty string (RETURN)
|
199
|
+
- Sub prompt (`:sub1` and `:sub2`) : always sends Empty string (RETURN)
|
151
200
|
|
152
201
|
## Contributing
|
153
202
|
|
data/Rakefile
CHANGED
@@ -18,3 +18,15 @@ rescue LoadError
|
|
18
18
|
$stderr.puts 'RuboCop is disabled'
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
begin
|
23
|
+
require 'yard'
|
24
|
+
require 'yard/rake/yardoc_task'
|
25
|
+
YARD::Rake::YardocTask.new do |task|
|
26
|
+
task.files = FileList['./lib/**/*.rb']
|
27
|
+
end
|
28
|
+
rescue LoadError
|
29
|
+
task :yard do
|
30
|
+
$stderr.puts 'YARD is disabled'
|
31
|
+
end
|
32
|
+
end
|
data/exe/run_command
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
require 'expectacle'
|
5
5
|
require 'optparse'
|
6
6
|
require 'yaml'
|
7
|
+
require 'syslog/logger'
|
7
8
|
|
8
9
|
option = {}
|
9
10
|
opt = OptionParser.new
|
@@ -22,16 +23,24 @@ end
|
|
22
23
|
opt.on('-r', '--run', 'Run(exec) commands to each hosts') do |value|
|
23
24
|
option[:run] = value
|
24
25
|
end
|
26
|
+
opt.on('-s', '--syslog', 'Use syslog logger (use localhost syslog)') do |value|
|
27
|
+
option[:syslog] = value
|
28
|
+
end
|
25
29
|
opt.parse!(ARGV)
|
26
30
|
|
27
|
-
file_dir = File.dirname(File.expand_path(__FILE__))
|
28
31
|
base_dir = if option.key?(:base_dir)
|
29
32
|
option[:base_dir]
|
30
33
|
else
|
34
|
+
file_dir = File.dirname(File.expand_path(__FILE__))
|
31
35
|
File.expand_path('../vendor', file_dir)
|
32
36
|
end
|
33
37
|
thrower = Expectacle::Thrower.new(base_dir: base_dir)
|
34
38
|
|
39
|
+
if option[:syslog]
|
40
|
+
thrower.logger = Syslog::Logger.new 'Expectacle'
|
41
|
+
thrower.setup_logger
|
42
|
+
end
|
43
|
+
|
35
44
|
hosts = YAML.load_file(File.join(thrower.hosts_dir, option[:hosts]))
|
36
45
|
commands = YAML.load_file(File.join(thrower.commands_dir, option[:commands]))
|
37
46
|
if option[:preview]
|
data/lib/expectacle/thrower.rb
CHANGED
@@ -5,14 +5,20 @@ require 'expectacle/thrower_base'
|
|
5
5
|
module Expectacle
|
6
6
|
# Thrower logic(command list operation)
|
7
7
|
class Thrower < ThrowerBase
|
8
|
+
# Run(exec) commands for all hosts.
|
9
|
+
# @param [Array<Hash>] hosts Host parameters (read from host list file).
|
10
|
+
# @param [Array<String>] commands Commands (read from command list file).
|
8
11
|
def run_command_for_all_hosts(hosts, commands)
|
9
|
-
@commands = commands
|
10
12
|
hosts.each do |each|
|
13
|
+
@commands = commands.dup # Notice: @commands will be decremented.
|
11
14
|
@host_param = each
|
12
15
|
run_command_for_host
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
19
|
+
# Preview all parameters for all hosts.
|
20
|
+
# @param [Array<Hash>] hosts Host parameters (read from host list file).
|
21
|
+
# @param [Array<String>] commands Commands (read from command list file).
|
16
22
|
def preview_parameter(hosts, commands)
|
17
23
|
@commands = commands
|
18
24
|
hosts.each do |each|
|
@@ -33,7 +39,7 @@ module Expectacle
|
|
33
39
|
|
34
40
|
def preview_command_for_host
|
35
41
|
ready_to_open_host_session do |spawn_cmd|
|
36
|
-
|
42
|
+
print YAML.dump(whole_previewed_parameters(spawn_cmd))
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
@@ -44,29 +50,29 @@ module Expectacle
|
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
47
|
-
def
|
53
|
+
def previewed_host_param
|
48
54
|
host_param = @host_param.dup
|
49
55
|
enable_mode = @enable_mode
|
50
56
|
@enable_mode = false
|
51
|
-
host_param[:username] = embed_user_name
|
52
|
-
host_param[:password] = embed_password
|
57
|
+
host_param[:username] = embed_user_name
|
58
|
+
host_param[:password] = embed_password
|
53
59
|
@enable_mode = true
|
54
|
-
host_param[:enable] = embed_password
|
60
|
+
host_param[:enable] = embed_password
|
55
61
|
@enable_mode = enable_mode
|
56
62
|
host_param
|
57
63
|
end
|
58
64
|
|
59
|
-
def
|
60
|
-
@commands.map { |cmd| embed_command(cmd
|
65
|
+
def previewed_commands
|
66
|
+
@commands.map { |cmd| embed_command(cmd) }
|
61
67
|
end
|
62
68
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
69
|
+
def whole_previewed_parameters(spawn_cmd)
|
70
|
+
{
|
71
|
+
spawn_cmd: spawn_cmd,
|
72
|
+
prompt: @prompt,
|
73
|
+
host: previewed_host_param,
|
74
|
+
commands: previewed_commands
|
75
|
+
}
|
70
76
|
end
|
71
77
|
|
72
78
|
def exec_rest_commands
|
@@ -81,7 +87,7 @@ module Expectacle
|
|
81
87
|
exec_rest_commands do
|
82
88
|
# Notice: @commands changed
|
83
89
|
command = @commands.shift
|
84
|
-
write_and_logging 'Send command: ', embed_command(command
|
90
|
+
write_and_logging 'Send command: ', embed_command(command)
|
85
91
|
end
|
86
92
|
end
|
87
93
|
|
@@ -114,9 +120,9 @@ module Expectacle
|
|
114
120
|
def exec_each_prompt(prompt)
|
115
121
|
case prompt
|
116
122
|
when /#{@prompt[:password]}/, /#{@prompt[:enable_password]}/
|
117
|
-
write_and_logging 'Send password', embed_password
|
123
|
+
write_and_logging 'Send password', embed_password, true
|
118
124
|
when /#{@prompt[:username]}/
|
119
|
-
write_and_logging 'Send username: ', embed_user_name
|
125
|
+
write_and_logging 'Send username: ', embed_user_name
|
120
126
|
when /#{@prompt[:command2]}/, /#{@prompt[:command1]}/
|
121
127
|
exec_by_mode(prompt)
|
122
128
|
when /#{@prompt[:yn]}/, /#{@prompt[:sub1]}/, /#{@prompt[:sub2]}/
|
@@ -9,11 +9,23 @@ require 'logger'
|
|
9
9
|
module Expectacle
|
10
10
|
# Basic state setup/management
|
11
11
|
class ThrowerBase
|
12
|
+
# @return [Logger] Logger instance.
|
12
13
|
attr_accessor :logger
|
14
|
+
# @return [String] Base directory path to find params/hosts/commands file.
|
13
15
|
attr_reader :base_dir
|
14
16
|
|
17
|
+
# Constructor
|
18
|
+
# @param timeout [Integer] Seconds to timeout. (default: 60sec)
|
19
|
+
# @param verbose [Boolean] Flag to enable verbose output.
|
20
|
+
# (default: `true`)
|
21
|
+
# @param base_dir [String] Base directory to find files.
|
22
|
+
# (default: `Dir.pwd`)
|
23
|
+
# @param logger [IO] IO Object to logging. (default `$stdout`)
|
24
|
+
# @return [Expectacle::ThrowerBase]
|
15
25
|
def initialize(timeout: 60, verbose: true,
|
16
26
|
base_dir: Dir.pwd, logger: $stdout)
|
27
|
+
# default
|
28
|
+
@host_param = {}
|
17
29
|
# remote connection timeout (sec)
|
18
30
|
@timeout = timeout
|
19
31
|
# cli mode flag
|
@@ -23,23 +35,44 @@ module Expectacle
|
|
23
35
|
# base dir
|
24
36
|
@base_dir = File.expand_path(base_dir)
|
25
37
|
# logger
|
26
|
-
|
38
|
+
@logger = Logger.new(logger)
|
39
|
+
setup_default_logger
|
27
40
|
end
|
28
41
|
|
42
|
+
# Path to prompt file directory.
|
43
|
+
# @return [String]
|
29
44
|
def prompts_dir
|
30
45
|
File.join @base_dir, 'prompts'
|
31
46
|
end
|
32
47
|
|
48
|
+
# Path to host list file directory.
|
49
|
+
# @return [String]
|
33
50
|
def hosts_dir
|
34
51
|
File.join @base_dir, 'hosts'
|
35
52
|
end
|
36
53
|
|
54
|
+
# Path to command list file directory.
|
55
|
+
# @return [String]
|
37
56
|
def commands_dir
|
38
57
|
File.join @base_dir, 'commands'
|
39
58
|
end
|
40
59
|
|
60
|
+
# Setup common settings of logger instance.
|
61
|
+
def setup_logger
|
62
|
+
@logger.level = Logger::INFO
|
63
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
64
|
+
"#{datetime} #{progname} [#{severity}] #{msg}\n"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
41
68
|
private
|
42
69
|
|
70
|
+
def setup_default_logger
|
71
|
+
@logger.progname = 'Expectacle'
|
72
|
+
@logger.datetime_format = '%Y-%m-%d %H:%M:%D %Z'
|
73
|
+
setup_logger
|
74
|
+
end
|
75
|
+
|
43
76
|
def ready_to_open_host_session
|
44
77
|
# prompt regexp of device
|
45
78
|
load_prompt_file
|
@@ -78,7 +111,7 @@ module Expectacle
|
|
78
111
|
['ssh',
|
79
112
|
'-o StrictHostKeyChecking=no',
|
80
113
|
'-o KexAlgorithms=+diffie-hellman-group1-sha1', # for old cisco device
|
81
|
-
"-l #{embed_user_name
|
114
|
+
"-l #{embed_user_name}",
|
82
115
|
@host_param[:ipaddr]].join(' ')
|
83
116
|
end
|
84
117
|
|
@@ -106,16 +139,6 @@ module Expectacle
|
|
106
139
|
@prompt = load_yaml_file('prompt file', prompt_file)
|
107
140
|
end
|
108
141
|
|
109
|
-
def setup_default_logger(logger_io)
|
110
|
-
@logger = Logger.new(logger_io)
|
111
|
-
@logger.level = Logger::INFO
|
112
|
-
@logger.progname = 'Expectacle'
|
113
|
-
@logger.datetime_format = '%Y-%m-%d %H:%M:%D %Z'
|
114
|
-
@logger.formatter = proc do |severity, datetime, progname, msg|
|
115
|
-
"#{datetime} #{progname} [#{severity}] #{msg}\n"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
142
|
def expect_regexp
|
120
143
|
/
|
121
144
|
( #{@prompt[:password]} | #{@prompt[:enable_password]}
|
@@ -143,7 +166,7 @@ module Expectacle
|
|
143
166
|
end
|
144
167
|
end
|
145
168
|
|
146
|
-
def embed_password
|
169
|
+
def embed_password
|
147
170
|
@host_param[:enable] = '_NOT_DEFINED_' unless @host_param.key?(:enable)
|
148
171
|
base_str = @enable_mode ? @host_param[:enable] : @host_param[:password]
|
149
172
|
check_embed_envvar(base_str)
|
@@ -151,12 +174,12 @@ module Expectacle
|
|
151
174
|
passwd_erb.result(binding)
|
152
175
|
end
|
153
176
|
|
154
|
-
def embed_command(command
|
177
|
+
def embed_command(command)
|
155
178
|
command_erb = ERB.new(command)
|
156
179
|
command_erb.result(binding)
|
157
180
|
end
|
158
181
|
|
159
|
-
def embed_user_name
|
182
|
+
def embed_user_name
|
160
183
|
check_embed_envvar(@host_param[:username])
|
161
184
|
uname_erb = ERB.new(@host_param[:username])
|
162
185
|
uname_erb.result(binding)
|
data/lib/expectacle/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: expectacle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- stereocat
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-10-
|
11
|
+
date: 2016-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -37,6 +37,7 @@ files:
|
|
37
37
|
- ".rspec"
|
38
38
|
- ".rubocop.yml"
|
39
39
|
- ".rubocop_todo.yml"
|
40
|
+
- ".yardopts"
|
40
41
|
- Gemfile
|
41
42
|
- LICENSE.txt
|
42
43
|
- README.md
|