Skip to content

Commit

Permalink
Refactor and Write yardoc for private methods
Browse files Browse the repository at this point in the history
  • Loading branch information
stereocat committed May 24, 2018
1 parent c2921a7 commit a67faca
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 103 deletions.
1 change: 1 addition & 0 deletions .yardopts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
--markup-provider=redcarpet
--markup=markdown
--private
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,16 +228,16 @@ Host *
Ciphers +aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc
```
## Use Local Serial Console
## Use Local Serial Port
Expectacle can handle `cu` (call up another system) command to operate via device local serial port.
If you use Ubuntu, install `cu` at first.
At first, install `cu`. If you use Ubuntu, install it with `apt`.
```
sudo apt install cu
```
Set parameter `:protocol` to `cu`, and write `cu` command options as `:cu_opts`. Usually, one serial port correspond to one device. So host parameter `:cu_opts` is used as options to connect a host via serial port. For example:
Next, set parameter `:protocol` to `cu`, and write `cu` command options as `:cu_opts`. Usually, one serial port correspond to one device. So host parameter `:cu_opts` is used as options to connect a host via serial port. For example:
```
- :hostname : 'l2sw1'
:type : 'c3750g'
Expand Down
95 changes: 43 additions & 52 deletions lib/expectacle/thrower.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,60 @@
require 'expectacle/thrower_utils'

module Expectacle
# Maximum number to retly authentication
MAX_AUTH_COUNT = 10

# Thrower logic(command list operation)
class Thrower < ThrowerBase
# #run_command_for_all_host [for hosts]
# - initialize command list (`@commands`)
# and host params (`@host_param`)
# #run_command_for_host [for a host] #-> thrower_base
# ...
# #exec_each_prompt #-> override

# Run(exec) commands for all hosts.
# @param [Array<Hash>] hosts Host parameters (read from host list file).
# @param [Array<String>] commands Commands (read from command list file).
def run_command_for_all_hosts(hosts, commands)
hosts.each do |each|
@commands = commands.dup # Notice: @commands will be decremented.
@commands_len = @commands.length
@auth_count = 0
@host_param = each
clear_auth_count
run_command_for_host
end
end

private

def run_command_for_host
ready_to_open_host_session do |spawn_cmd|
open_interactive_process(spawn_cmd) do
before_run_command
run_command
end
end
end

def before_run_command
return unless @local_serial
# for `cu` command
@reader.expect(/^Connected\./, 1) do
write_and_logging 'Send enter to connect serial', "\r\n", true
end
end

def run_command
do_on_interactive_process do |match|
@logger.debug "Read: #{match}"
exec_each_prompt match[1]
# Send command when found prompt
# @param prompt [String] Prompt
def exec_each_prompt(prompt)
check_auth_count
case prompt
when /#{@prompt[:password]}/, /#{@prompt[:enable_password]}/
write_and_logging 'Send password', embed_password, true
when /#{@prompt[:username]}/
write_and_logging 'Send username: ', embed_user_name
when /#{@prompt[:command2]}/, /#{@prompt[:command1]}/
exec_by_mode(prompt)
when /#{@prompt[:yn]}/, /#{@prompt[:sub1]}/, /#{@prompt[:sub2]}/
exec_by_sub_prompt(prompt)
else
@logger.error "Unknown prompt #{prompt}"
end
end

def exec_rest_commands
if !@commands.empty?
yield
else
close_session
# Send command according to exec mode
# @param prompt [String] Prompt
def exec_by_mode(prompt)
case prompt
when /#{@prompt[:command2]}/
exec_in_privilege_mode
when /#{@prompt[:command1]}/
exec_in_normal_mode
end
end

# Send command in priviledge mode
def exec_in_privilege_mode
exec_rest_commands do
# Notice: @commands changed
Expand All @@ -64,22 +66,27 @@ def exec_in_privilege_mode
end
end

# Send command in normal (non-priviledge) mode
def exec_in_normal_mode
exec_rest_commands do
write_and_logging 'Send enable command: ', @prompt[:enable_command]
@enable_mode = true
end
end

def exec_by_mode(prompt)
case prompt
when /#{@prompt[:command2]}/
exec_in_privilege_mode
when /#{@prompt[:command1]}/
exec_in_normal_mode
# Check command list is empty or not.
# If command list is empty, then close session
# @yield [] Operations when command list is not empty
def exec_rest_commands
if !@commands.empty?
yield
else
close_session
end
end

# Send command in sub-prompt
# @param prompt [String] Prompt
def exec_by_sub_prompt(prompt)
case prompt
when /#{@prompt[:yn]}/
Expand All @@ -89,21 +96,5 @@ def exec_by_sub_prompt(prompt)
write_and_logging 'Send return: ', ''
end
end

def exec_each_prompt(prompt)
check_auth_count
case prompt
when /#{@prompt[:password]}/, /#{@prompt[:enable_password]}/
write_and_logging 'Send password', embed_password, true
when /#{@prompt[:username]}/
write_and_logging 'Send username: ', embed_user_name
when /#{@prompt[:command2]}/, /#{@prompt[:command1]}/
exec_by_mode(prompt)
when /#{@prompt[:yn]}/, /#{@prompt[:sub1]}/, /#{@prompt[:sub2]}/
exec_by_sub_prompt(prompt)
else
@logger.error "Unknown prompt #{prompt}"
end
end
end
end
91 changes: 60 additions & 31 deletions lib/expectacle/thrower_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,32 +36,31 @@ def initialize(timeout: 60, verbose: true,
setup_default_logger(logger)
end

# Path to prompt file directory.
# @return [String]
def prompts_dir
File.join @base_dir, 'prompts'
end

# Path to host list file directory.
# @return [String]
def hosts_dir
File.join @base_dir, 'hosts'
end
private

# Path to command list file directory.
# @return [String]
def commands_dir
File.join @base_dir, 'commands'
end
# #run_command_for_host [for a host]
# #ready_to_open_host_session
# - initialize prompts (`@prompt`), `spawn_cmd` string
# #open_interactive_process
# - spawn and setup @reader/@writer
# #run_command
# #do_on_interactive_process
# - expect
# #exec_each_prompt (will be overriden)

# Path to span command options file directory.
# @return [String]
def opts_dir
File.join @base_dir, 'opts'
# Run(Send) command to host(interactive process)
def run_command_for_host
ready_to_open_host_session do |spawn_cmd|
open_interactive_process(spawn_cmd) do
before_run_command
run_command
end
end
end

private

# Setup a parameters for interactive process
# @yield [spawn_cmd] Operations for interactive process
# @yieldparam spawn_cmd [String] Command of interactive process
def ready_to_open_host_session
@local_serial = false # default for host
load_prompt_file # prompt regexp of device
Expand All @@ -73,6 +72,39 @@ def ready_to_open_host_session
end
end

# Spawn interactive process
# @yield [] Operations for interactive process
def open_interactive_process(spawn_cmd)
@logger.info "Begin spawn: #{spawn_cmd}"
PTY.spawn(spawn_cmd) do |reader, writer, _pid|
@enable_mode = false
@reader = reader
@writer = writer
@writer.sync = true
yield
end
end

# Pre-process before send command
def before_run_command
return unless @local_serial
# for `cu` command
@reader.expect(/^Connected\./, 1) do
write_and_logging 'Send enter to connect serial', "\r\n", true
end
end

# Send command to host(interactive process)
def run_command
do_on_interactive_process do |match|
@logger.debug "Read: #{match}"
exec_each_prompt match[1]
end
end

# Search prompt and send command, while process is opened
# @yield [match] Send operations when found prompt
# @yieldparam match [String] Expect matches string (prompt)
def do_on_interactive_process
until @reader.closed? || @reader.eof?
@reader.expect(expect_regexp, @timeout) do |match|
Expand All @@ -84,15 +116,12 @@ def do_on_interactive_process
@logger.debug "PTY raises Errno::EIO, #{error.message}"
end

def open_interactive_process(spawn_cmd)
@logger.info "Begin spawn: #{spawn_cmd}"
PTY.spawn(spawn_cmd) do |reader, writer, _pid|
@enable_mode = false
@reader = reader
@writer = writer
@writer.sync = true
yield
end
# Send command when found prompt
# @abstract Subclass must override to send command (per prompt)
# @param _prompt [String] Prompt
# @raise [Error]
def exec_each_prompt(_prompt)
raise 'Called abstract method'
end
end
end
15 changes: 15 additions & 0 deletions lib/expectacle/thrower_base_io.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ class ThrowerBase

private

# Send string to process and logging the string
# @param message [String] Message to logging
# @param command [String] Command to send
# @param secret [Boolearn] Choise to logging command
def write_and_logging(message, command, secret = false)
logging_message = secret ? message : message + command
@logger.info logging_message
Expand All @@ -23,13 +27,17 @@ def write_and_logging(message, command, secret = false)
end
end

# Setup default IO logger
# @return [Logger] logger
def default_io_logger(logger_io, progname)
logger = Logger.new(logger_io)
logger.progname = progname
logger.datetime_format = '%Y-%m-%d %H:%M:%D %Z'
logger
end

# Setup default logger (select IO or Syslog logger)
# @param logger [Symbol] Syslog logger or not
def setup_default_logger(logger)
progname = 'Expectacle'
@logger = if logger == :syslog
Expand All @@ -43,18 +51,25 @@ def setup_default_logger(logger)
end
end

# YAML file loader
# @param file_type [String] File description
# @param file_name [String] File name to load
# @raise [Error] File load error
def load_yaml_file(file_type, file_name)
YAML.load_file file_name
rescue StandardError => error
@logger.error "Cannot load #{file_type}: #{file_name}"
raise error
end

# Load prompt file and setup prompt parameter
def load_prompt_file
prompt_file = "#{prompts_dir}/#{@host_param[:type]}_prompt.yml"
@prompt = load_yaml_file('prompt file', prompt_file)
end

# Load span command options from file
# @return [Array<String>] Options for spawn command
def load_spawn_command_opts_file
opts_file = "#{opts_dir}/#{@host_param[:protocol]}_opts.yml"
if File.exist?(opts_file)
Expand Down
Loading

0 comments on commit a67faca

Please sign in to comment.