expectacle 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0d1decbec1db35ee28a8b21210f8f61c42cff269
4
+ data.tar.gz: 33563494544b430040b0c1488280f48474a48062
5
+ SHA512:
6
+ metadata.gz: fc8348cd4cf0fecc78dc5b72ad45169d144d01d1a5c467236cf661e3492c0a2782579b4e97be562ea4fe4a8db7dabfef314c47d65a14556f5635f1c686ddd4b7
7
+ data.tar.gz: e870762f009586844c50b4ea06e5a7bed8179a94d06800542a602599d90b80bf6e1a4e9f3ad4966797f61b241459dbfc29ac58941f8aa52cffbe1e2a327aeb8d
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.idea/
11
+ /vendor/bundle/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,9 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ Metrics/LineLength:
4
+ Exclude:
5
+ - 'spec/**/*_spec.rb'
6
+
7
+ Style/BlockComments:
8
+ Exclude:
9
+ - 'spec/spec_helper.rb'
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,27 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2016-10-26 12:34:05 +0900 using RuboCop version 0.44.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ Metrics/AbcSize:
11
+ Max: 18
12
+
13
+ # Offense count: 2
14
+ # Configuration parameters: CountComments.
15
+ Metrics/ClassLength:
16
+ Max: 140
17
+
18
+ # Offense count: 1
19
+ # Configuration parameters: CountComments.
20
+ Metrics/MethodLength:
21
+ Max: 12
22
+
23
+ # Offense count: 1
24
+ # Configuration parameters: AllowedVariables.
25
+ Style/GlobalVars:
26
+ Exclude:
27
+ - 'lib/expectacle/thrower_base.rb'
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in expectacle.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rspec'
8
+ gem 'rspec-given'
9
+ gem 'simplecov'
10
+ end
11
+
12
+ gem 'rubocop'
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 TODO: Write your name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # Expectacle
2
+
3
+ Expectacle ("expect + spectacle") is a small wrapper of `pty`/`expect`.
4
+ It can send commands (command-list) to hosts (including network devices etc)
5
+ using telnet/ssh session.
6
+
7
+ Expectacle is portable (instead of less feature).
8
+ Because it depends on only standard modules (YAML, ERB, PTY, Expect and Logger).
9
+ It can work on almost ruby(>2.2) system without installation other gems. (probably...)
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'expectacle'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install expectacle
26
+
27
+ ## Usage
28
+
29
+ ### Utility Script
30
+ See `bin/run_command` and `vendor` directory.
31
+
32
+ `run_command` can send commands to hosts.
33
+
34
+ $ bundle exec ./bin/run_command l2switch.yml cisco_show_arp.yml
35
+
36
+ - `l2switch.yml` is host-list file.
37
+ It is a data definitions for each hosts to send commands.
38
+ - At username and password (login/enable) parameter,
39
+ you can write environment variables with ERB manner to avoid write raw login information.
40
+ - `bin/readne` is a small utility shell-script to set environment variable from CLI.
41
+
42
+ ```
43
+ $ export L2SW_USER=`./bin/readne`
44
+ Input: (type username)
45
+ $ export L2SW_PASS=`./bin/readne`
46
+ Input: (type password)
47
+ ```
48
+
49
+ - `Expectacle::Thrower` read prompt-file by "type" parameter in host-list file.
50
+ - In prompt-file, prompt regexps that used for interactive operation to host
51
+ are defined. (These regexp are common information for some host-groups. (vendor, OS, ...))
52
+ - Prompt-file is searched by filename: `#{type}_prompt.yml`,
53
+ `type` parameter defined in host-list file.
54
+ - `cisco_show_arp.yml` is command-list file.
55
+ - it is a list of commands.
56
+ - Each files are written by YAML.
57
+
58
+ ## Parameter Definitions
59
+
60
+ ### Expectacle::Thrower
61
+
62
+ `Expectacle::Thrower` argument description.
63
+ - `:timeout` : (Optional) Timeout interval (sec) to connect a host.
64
+ (default: 60sec)
65
+ - `:verbose` : (Optional) When `:verbose` is `false`,
66
+ `Expectacle` does not output spawned process input/output to standard-out(`$stdout`).
67
+ (default: `true`)
68
+ - `:base_dir`: (Optional) Base path to search host/prompt/command files.
69
+ (default: current working directory (`Dir.pwd`))
70
+ - `#{base_dir}/commands`: command-list file directory.
71
+ - `#{base_dir}/prompts` : prompt-file directory.
72
+ - `#{base_dir}/hosts` : host-file directory.
73
+ - `:logger` : (Optional) IO object to logging `Expectacle` operations.
74
+ (default: `$stdout`)
75
+
76
+ **Notice** : When `Expectacle` success to connect(spawn) host,
77
+ it will change the user mode to privilege (root/super-user/enable) at first, ASAP.
78
+ All commands are executed with privilege mode at the host.
79
+
80
+ ### Host-list parameter
81
+ Host-list file is a list of host-parameters.
82
+ - `:hostname`: Indication String of host name.
83
+ - `:type`: Host type (used to choose prompt-file).
84
+ - `:ipaddr`: IP(v4) address to connect host.
85
+ - `:protocol`: Protocol to connect host. (telnet or ssh)
86
+ - `:username`: Login name.
87
+ - `:password`: Login password.
88
+ - `:enable`: Password to be privilege mode.
89
+
90
+ It can use ERB to set values from environment variable in `:username`, `:password` and `:enable`.
91
+
92
+ You can add other parameter(s) to refer in command-list files.
93
+ See also: "Command list" section.
94
+
95
+ ### Prompt parameter
96
+ Prompt file is a table of prompt regexp of host group(type).
97
+ - `:password`: Login password prompt
98
+ - `:username`: Login username prompt
99
+ - `:sub1`: Sub command prompt
100
+ - `:sub2`: Sub command prompt
101
+ - `:yn`: Yes/No prompt
102
+ - `:command1`: Command prompt (normal mode)
103
+ - `:command2`: Command prompt (privilege mode)
104
+ - `enable_password`: Enable password prompt
105
+ - `enable_command`: command to be privilege mode
106
+ (Only this parameter is not a "prompt regexp")
107
+
108
+ ### Command list with ERB
109
+ Command-list is a simple list of command-string.
110
+ A command-string can contain host-parameter reference by ERB.
111
+
112
+ For example, if you want to save configuration of a Cisco device to tftp server:
113
+ - Add a parameter to tftp server info (IP address) in host-list file. (`vendor/hosts/l2switch.yml`)
114
+ ```YAML
115
+ - :hostname : 'l2sw1'
116
+ :type : 'c3750g'
117
+ :ipaddr : '192.168.0.1'
118
+ :protocol : 'ssh'
119
+ :username : "<%= ENV['L2SW_USER'] %>"
120
+ :password : "<%= ENV['L2SW_PASS'] %>"
121
+ :enable : "<%= ENV['L2SW_PASS'] %>"
122
+ :tftp_server: '192.168.2.16'
123
+ - :hostname : 'l2sw2'
124
+ :type : 'c3750g'
125
+ :ipaddr : '192.168.0.2'
126
+ :protocol : 'ssh'
127
+ :username : "<%= ENV['L2SW_USER'] %>"
128
+ :password : "<%= ENV['L2SW_PASS'] %>"
129
+ :enable : "<%= ENV['L2SW_PASS'] %>"
130
+ :tftp_server: '192.168.2.16'
131
+ ```
132
+
133
+ - Write command-list file using ERB.
134
+ When send a command to host, ERB string was evaluated in `Expectacle::Thrower` bindings.
135
+ Then, it can refer host-parameter as `@host_param` hash. (`vendor/commands/cisco_save_config_tftp.yml`)
136
+ - When exec below command-list, host configuration will be saved a file as `l2sw1.confg` on tftp server.
137
+
138
+ ```YAML
139
+ - "copy run start"
140
+ - "copy run tftp://<%= @host_param[:tftp_server] %>/<%= @host_param[:hostname] %>.confg"
141
+ ```
142
+
143
+ ## TODO
144
+
145
+ ### Sub prompt operation (interactive command)
146
+ Feature for sub-prompt (interactive command) is not enough.
147
+ Now, Expectacle sends fixed command for sub-prompt.
148
+ (These actions were defined for cisco to execute above "copy run" example...)
149
+ - Yex/No (`:yn`) : always sends "yes"
150
+ - Sub prompt (`:sub1` and `:sub2`) : Empty string (RETURN)
151
+
152
+ ## Contributing
153
+
154
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/stereocat/expectacle>.
155
+
156
+ ## License
157
+
158
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require 'bundler/gem_tasks'
2
+ task default: [:spec, :rubocop]
3
+
4
+ begin
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new
7
+ rescue LoadError
8
+ task :spec do
9
+ $stderr.puts 'RSpec is disabled'
10
+ end
11
+ end
12
+
13
+ begin
14
+ require 'rubocop/rake_task'
15
+ RuboCop::RakeTask.new
16
+ rescue LoadError
17
+ task :rubocop do
18
+ $stderr.puts 'RuboCop is disabled'
19
+ end
20
+ end
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/readne ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+ read -sp 'Input: ' pass
3
+ echo $pass
data/exe/run_command ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'expectacle'
5
+ require 'optparse'
6
+ require 'yaml'
7
+
8
+ option = {}
9
+ opt = OptionParser.new
10
+ opt.on('-h', '--hosts=FILE', 'Host list file') do |value|
11
+ option[:hosts] = value
12
+ end
13
+ opt.on('-c', '--commands=FILE', 'Command list file') do |value|
14
+ option[:commands] = value
15
+ end
16
+ opt.on('-b', '--base-dir=DIR', 'Base directory path') do |value|
17
+ option[:base_dir] = value
18
+ end
19
+ opt.on('-p', '--preview', 'Preview parameter') do |value|
20
+ option[:preview] = value
21
+ end
22
+ opt.on('-r', '--run', 'Run(exec) commands to each hosts') do |value|
23
+ option[:run] = value
24
+ end
25
+ opt.parse!(ARGV)
26
+
27
+ file_dir = File.dirname(File.expand_path(__FILE__))
28
+ base_dir = if option.key?(:base_dir)
29
+ option[:base_dir]
30
+ else
31
+ File.expand_path('../vendor', file_dir)
32
+ end
33
+ thrower = Expectacle::Thrower.new(base_dir: base_dir)
34
+
35
+ hosts = YAML.load_file(File.join(thrower.hosts_dir, option[:hosts]))
36
+ commands = YAML.load_file(File.join(thrower.commands_dir, option[:commands]))
37
+ if option[:preview]
38
+ thrower.preview_parameter(hosts, commands)
39
+ elsif option[:run]
40
+ thrower.run_command_for_all_hosts(hosts, commands)
41
+ else
42
+ STDERR.puts "#{$PROGRAM_NAME}: Action(preview/run) did not specified."
43
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'expectacle/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'expectacle'
8
+ spec.version = Expectacle::VERSION
9
+ spec.authors = ['stereocat']
10
+ spec.email = ['stereocat@gmail.com']
11
+
12
+ spec.summary = 'Simple expect wrapper to send commands to a devices.'
13
+ spec.description = 'Expectacle is simple wrapper of pty/expect.'
14
+ spec.homepage = 'https://github.com/stereocat/expectacle'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'expectacle/thrower_base'
4
+
5
+ module Expectacle
6
+ # Thrower logic(command list operation)
7
+ class Thrower < ThrowerBase
8
+ def run_command_for_all_hosts(hosts, commands)
9
+ @commands = commands
10
+ hosts.each do |each|
11
+ @host_param = each
12
+ run_command_for_host
13
+ end
14
+ end
15
+
16
+ def preview_parameter(hosts, commands)
17
+ @commands = commands
18
+ hosts.each do |each|
19
+ @host_param = each
20
+ preview_command_for_host
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def run_command_for_host
27
+ ready_to_open_host_session do |spawn_cmd|
28
+ open_interactive_process(spawn_cmd) do
29
+ run_command
30
+ end
31
+ end
32
+ end
33
+
34
+ def preview_command_for_host
35
+ ready_to_open_host_session do |spawn_cmd|
36
+ preview_command spawn_cmd
37
+ end
38
+ end
39
+
40
+ def run_command
41
+ do_on_interactive_process do |match|
42
+ @logger.debug "Read: #{match}"
43
+ exec_each_prompt match[1]
44
+ end
45
+ end
46
+
47
+ def preview_host_param
48
+ host_param = @host_param.dup
49
+ enable_mode = @enable_mode
50
+ @enable_mode = false
51
+ host_param[:username] = embed_user_name(binding)
52
+ host_param[:password] = embed_password(binding)
53
+ @enable_mode = true
54
+ host_param[:enable] = embed_password(binding)
55
+ @enable_mode = enable_mode
56
+ host_param
57
+ end
58
+
59
+ def preview_commands
60
+ @commands.map { |cmd| embed_command(cmd, binding) }
61
+ end
62
+
63
+ def preview_command(spawn_cmd)
64
+ data = {}
65
+ data[:spawn_cmd] = spawn_cmd
66
+ data[:prompt] = @prompt
67
+ data[:host] = preview_host_param
68
+ data[:commands] = preview_commands
69
+ print YAML.dump(data)
70
+ end
71
+
72
+ def exec_rest_commands
73
+ if !@commands.empty?
74
+ yield
75
+ else
76
+ write_and_logging 'Send break: ', 'exit'
77
+ end
78
+ end
79
+
80
+ def exec_in_privilege_mode
81
+ exec_rest_commands do
82
+ # Notice: @commands changed
83
+ command = @commands.shift
84
+ write_and_logging 'Send command: ', embed_command(command, binding)
85
+ end
86
+ end
87
+
88
+ def exec_in_normal_mode
89
+ exec_rest_commands do
90
+ write_and_logging 'Send enable command: ', @prompt[:enable_command]
91
+ @enable_mode = true
92
+ end
93
+ end
94
+
95
+ def exec_by_mode(prompt)
96
+ case prompt
97
+ when /#{@prompt[:command2]}/
98
+ exec_in_privilege_mode
99
+ when /#{@prompt[:command1]}/
100
+ exec_in_normal_mode
101
+ end
102
+ end
103
+
104
+ def exec_by_sub_prompt(prompt)
105
+ case prompt
106
+ when /#{@prompt[:yn]}/
107
+ # it must match before sub_prompt
108
+ write_and_logging 'Send yes: ', 'yes'
109
+ when /#{@prompt[:sub1]}/, /#{@prompt[:sub2]}/
110
+ write_and_logging 'Send return: ', ''
111
+ end
112
+ end
113
+
114
+ def exec_each_prompt(prompt)
115
+ case prompt
116
+ when /#{@prompt[:password]}/, /#{@prompt[:enable_password]}/
117
+ write_and_logging 'Send password', embed_password(binding), true
118
+ when /#{@prompt[:username]}/
119
+ write_and_logging 'Send username: ', embed_user_name(binding)
120
+ when /#{@prompt[:command2]}/, /#{@prompt[:command1]}/
121
+ exec_by_mode(prompt)
122
+ when /#{@prompt[:yn]}/, /#{@prompt[:sub1]}/, /#{@prompt[:sub2]}/
123
+ exec_by_sub_prompt(prompt)
124
+ else
125
+ @logger.error "Unknown prompt #{prompt}"
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pty'
4
+ require 'expect'
5
+ require 'yaml'
6
+ require 'erb'
7
+ require 'logger'
8
+
9
+ module Expectacle
10
+ # Basic state setup/management
11
+ class ThrowerBase
12
+ attr_accessor :logger
13
+ attr_reader :base_dir
14
+
15
+ def initialize(timeout: 60, verbose: true,
16
+ base_dir: Dir.pwd, logger: $stdout)
17
+ # remote connection timeout (sec)
18
+ @timeout = timeout
19
+ # cli mode flag
20
+ @enable_mode = false
21
+ # debug (use debug print to stdout)
22
+ $expect_verbose = verbose
23
+ # base dir
24
+ @base_dir = File.expand_path(base_dir)
25
+ # logger
26
+ setup_default_logger(logger)
27
+ end
28
+
29
+ def prompts_dir
30
+ File.join @base_dir, 'prompts'
31
+ end
32
+
33
+ def hosts_dir
34
+ File.join @base_dir, 'hosts'
35
+ end
36
+
37
+ def commands_dir
38
+ File.join @base_dir, 'commands'
39
+ end
40
+
41
+ private
42
+
43
+ def ready_to_open_host_session
44
+ # prompt regexp of device
45
+ load_prompt_file
46
+ spawn_cmd = make_spawn_command
47
+ if @prompt && spawn_cmd
48
+ yield spawn_cmd
49
+ else
50
+ @logger.error 'Invalid parameter in param file(S)'
51
+ end
52
+ end
53
+
54
+ def do_on_interactive_process
55
+ until @reader.eof?
56
+ @reader.expect(expect_regexp, @timeout) do |match|
57
+ yield match
58
+ end
59
+ end
60
+ rescue Errno::EIO => error
61
+ # on linux, PTY raises Errno::EIO when spawned process closed.
62
+ @logger.debug "PTY raises Errno::EIO, #{error.message}"
63
+ end
64
+
65
+ def open_interactive_process(spawn_cmd)
66
+ @logger.info "Begin spawn: #{spawn_cmd}"
67
+ PTY.spawn(spawn_cmd) do |reader, writer, _pid|
68
+ @enable_mode = false
69
+ @reader = reader
70
+ @writer = writer
71
+ @writer.sync = true
72
+ yield
73
+ end
74
+ @logger.info "End spawn: #{@host_param[:hostname]}"
75
+ end
76
+
77
+ def ssh_command
78
+ ['ssh',
79
+ '-o StrictHostKeyChecking=no',
80
+ '-o KexAlgorithms=+diffie-hellman-group1-sha1', # for old cisco device
81
+ "-l #{embed_user_name(binding)}",
82
+ @host_param[:ipaddr]].join(' ')
83
+ end
84
+
85
+ def make_spawn_command
86
+ case @host_param[:protocol]
87
+ when /^telnet$/i
88
+ ['telnet', @host_param[:ipaddr]].join(' ')
89
+ when /^ssh$/i
90
+ ssh_command
91
+ else
92
+ @logger.error "Unknown protocol #{@host_param[:protocol]}"
93
+ nil
94
+ end
95
+ end
96
+
97
+ def load_yaml_file(file_type, file_name)
98
+ YAML.load_file file_name
99
+ rescue StandardError => error
100
+ @logger.error "Cannot load #{file_type}: #{file_name}"
101
+ raise error
102
+ end
103
+
104
+ def load_prompt_file
105
+ prompt_file = "#{prompts_dir}/#{@host_param[:type]}_prompt.yml"
106
+ @prompt = load_yaml_file('prompt file', prompt_file)
107
+ end
108
+
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
+ def expect_regexp
120
+ /
121
+ ( #{@prompt[:password]} | #{@prompt[:enable_password]}
122
+ | #{@prompt[:username]}
123
+ | #{@prompt[:command1]} | #{@prompt[:command2]}
124
+ | #{@prompt[:sub1]} | #{@prompt[:sub2]}
125
+ | #{@prompt[:yn]}
126
+ )\s*$
127
+ /x
128
+ end
129
+
130
+ def write_and_logging(message, command, secret = false)
131
+ logging_message = secret ? message : message + command
132
+ @logger.info logging_message
133
+ @writer.puts command
134
+ end
135
+
136
+ def check_embed_envvar(command)
137
+ return unless command =~ /<%=\s*ENV\[[\'\"]?(.+)[\'\"]\]?\s*%>/
138
+ envvar_name = Regexp.last_match(1)
139
+ if !ENV.key?(envvar_name)
140
+ @logger.error "Variable name: #{envvar_name} is not found in ENV"
141
+ elsif ENV[envvar_name] =~ /^\s*$/
142
+ @logger.warn "Env var: #{envvar_name} exists, but null string"
143
+ end
144
+ end
145
+
146
+ def embed_password(binding)
147
+ @host_param[:enable] = '_NOT_DEFINED_' unless @host_param.key?(:enable)
148
+ base_str = @enable_mode ? @host_param[:enable] : @host_param[:password]
149
+ check_embed_envvar(base_str)
150
+ passwd_erb = ERB.new(base_str)
151
+ passwd_erb.result(binding)
152
+ end
153
+
154
+ def embed_command(command, binding)
155
+ command_erb = ERB.new(command)
156
+ command_erb.result(binding)
157
+ end
158
+
159
+ def embed_user_name(binding)
160
+ check_embed_envvar(@host_param[:username])
161
+ uname_erb = ERB.new(@host_param[:username])
162
+ uname_erb.result(binding)
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,3 @@
1
+ module Expectacle
2
+ VERSION = '0.0.1'.freeze
3
+ end
data/lib/expectacle.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'expectacle/version'
4
+ require 'expectacle/thrower'
5
+
6
+ # Expectable: simple pry/expect wrapper.
7
+ module Expectacle; end
@@ -0,0 +1,2 @@
1
+ - "copy run start"
2
+ - "copy run tftp://<%= @host_param[:tftp_server] %>/<%= @host_param[:hostname] %>.confg"
@@ -0,0 +1,4 @@
1
+ - 'show ip arp'
2
+ - 'show mac address-table dynamic'
3
+
4
+
@@ -0,0 +1,3 @@
1
+ - 'ovs-vsctl list-br'
2
+ - 'ovs-vsctl show'
3
+ - 'ovs-ofctl dump-flows br0'
@@ -0,0 +1,4 @@
1
+ - 'get arp'
2
+ - 'get mac-learn'
3
+
4
+
@@ -0,0 +1,2 @@
1
+ - "save config"
2
+ - "save config to tftp <%= @host_param[:tftp_server] %> <%= @host_param[:hostname] %>.confg from eth0/1"
@@ -0,0 +1,15 @@
1
+ - :hostname : 'fw1'
2
+ :type : 'ssg'
3
+ :ipaddr : '192.168.20.161'
4
+ :protocol : 'telnet'
5
+ :username : "<%= ENV['FW_USER'] %>"
6
+ :password : "<%= ENV['FW_PASS'] %>"
7
+ :tftp_server : '192.168.20.170'
8
+ - :hostname : 'fw2'
9
+ :type : 'ssg'
10
+ :ipaddr : '192.168.20.162'
11
+ :protocol : 'telnet'
12
+ :username : "<%= ENV['FW_USER'] %>"
13
+ :password : "<%= ENV['FW_PASS'] %>"
14
+ :tftp_server : '192.168.20.170'
15
+
@@ -0,0 +1,16 @@
1
+ - :hostname : 'l2sw1'
2
+ :type : 'c3750g'
3
+ :ipaddr : '192.168.20.150'
4
+ :protocol : 'ssh'
5
+ :username : "<%= ENV['L2SW_USER'] %>"
6
+ :password : "<%= ENV['L2SW_PASS'] %>"
7
+ :enable : "<%= ENV['L2SW_PASS'] %>"
8
+ :tftp_server: '192.168.20.170'
9
+ - :hostname : 'l2sw2'
10
+ :type : 'c3750g'
11
+ :ipaddr : '192.168.20.151'
12
+ :protocol : 'ssh'
13
+ :username : "<%= ENV['L2SW_USER'] %>"
14
+ :password : "<%= ENV['L2SW_PASS'] %>"
15
+ :enable : "<%= ENV['L2SW_PASS'] %>"
16
+ :tftp_server: '192.168.20.170'
@@ -0,0 +1,14 @@
1
+ - :hostname : 'ofs1'
2
+ :type : 'pica8'
3
+ :ipaddr : '192.168.20.155'
4
+ :protocol : 'ssh'
5
+ :username : "<%= ENV['OFS1_USER'] %>"
6
+ :password : "<%= ENV['OFS_PASS'] %>"
7
+ :enable : "<%= ENV['OFS_PASS'] %>"
8
+ - :hostname : 'ofs2'
9
+ :type : 'pica8'
10
+ :ipaddr : '192.168.20.156'
11
+ :protocol : 'ssh'
12
+ :username : "<%= ENV['OFS2_USER'] %>"
13
+ :password : "<%= ENV['OFS_PASS'] %>"
14
+ :enable : "<%= ENV['OFS_PASS'] %>"
@@ -0,0 +1,9 @@
1
+ :password : '^Password\s*:'
2
+ :username : '^Username\s*:'
3
+ :sub1 : '\][:\?]'
4
+ :sub2 : '\[confirm\]'
5
+ :yn : '\[yes\/no\]:'
6
+ :command1 : '^[\w\-]+>'
7
+ :command2 : '^[\w\-]+(:?\(config\))?\#'
8
+ :enable_password : 'SAME_AS_LOGIN_PASSWORD'
9
+ :enable_command : 'enable'
@@ -0,0 +1,9 @@
1
+ :password : '^.+password\s*:'
2
+ :username : 'DO_NOT_USE_FOR_PICA8'
3
+ :sub1 : 'DO_NOT_USE_FOR_PICA8'
4
+ :sub2 : 'DO_NOT_USE_FOR_PICA8'
5
+ :yn : 'DO_NOT_USE_FOR_PICA8'
6
+ :command1 : '^[\w]+@[\w\-]+\$' # username@host-name$
7
+ :command2 : '^(:?root|admin)@[\w\-]+\$'
8
+ :enable_password : '^Password:\s*'
9
+ :enable_command : 'su'
@@ -0,0 +1,9 @@
1
+ :password : '^password:\s*'
2
+ :username : '^login:\s*'
3
+ :sub1 : 'DO_NOT_USE_FOR_SSG'
4
+ :sub2 : 'DO_NOT_USE_FOR_SSG'
5
+ :yn : '\[yes\/no\]:'
6
+ :command1 : 'DO_NOT_USE_FOR_SSG'
7
+ :command2 : '^[\w\-]+:[\w\-]+\([A-Z]+\)'
8
+ :enable_password : 'DO_NOT_USE_FOR_SSG'
9
+ :enable_command: '' # none
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: expectacle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - stereocat
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-10-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10.0'
27
+ description: Expectacle is simple wrapper of pty/expect.
28
+ email:
29
+ - stereocat@gmail.com
30
+ executables:
31
+ - readne
32
+ - run_command
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".gitignore"
37
+ - ".rspec"
38
+ - ".rubocop.yml"
39
+ - ".rubocop_todo.yml"
40
+ - Gemfile
41
+ - LICENSE.txt
42
+ - README.md
43
+ - Rakefile
44
+ - bin/setup
45
+ - exe/readne
46
+ - exe/run_command
47
+ - expectacle.gemspec
48
+ - lib/expectacle.rb
49
+ - lib/expectacle/thrower.rb
50
+ - lib/expectacle/thrower_base.rb
51
+ - lib/expectacle/version.rb
52
+ - vendor/commands/cisco_save_config_tftp.yml
53
+ - vendor/commands/cisco_show_arp.yml
54
+ - vendor/commands/pica8_ovs_info.yml
55
+ - vendor/commands/ssg_get_arp.yml
56
+ - vendor/commands/ssg_save_config_tftp.yml
57
+ - vendor/hosts/fw.yml
58
+ - vendor/hosts/l2switch.yml
59
+ - vendor/hosts/ofswitch.yml
60
+ - vendor/prompts/c3750g_prompt.yml
61
+ - vendor/prompts/pica8_prompt.yml
62
+ - vendor/prompts/ssg_prompt.yml
63
+ homepage: https://github.com/stereocat/expectacle
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.5.1
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Simple expect wrapper to send commands to a devices.
87
+ test_files: []