Module: EasyIO
- Defined in:
- lib/easy_io/run.rb,
lib/easy_io/disk.rb,
lib/easy_io/config.rb,
lib/easy_io/logger.rb,
lib/easy_io/registry.rb,
lib/easy_io/terminal.rb
Defined Under Namespace
Modules: Disk, Registry, Terminal
Class Method Summary collapse
- ._full_error_message(error_message, error_options, command) ⇒ Object
- ._parse_for_errors(message, error_messages, error_options, command) ⇒ Object
- ._process_error_message(error_message, error_messages, error_options, command) ⇒ Object
- .add_as_winrm_trusted_host(remote_host) ⇒ Object
- .config ⇒ Object
- .defaults ⇒ Object
-
.execute_out(command, pid_logfile: nil, working_folder: Dir.pwd, regex_error_filters: [], info_on_exception: '', exception_exceptions: [], powershell: false, show_command_on_error: false, raise_on_first_error: true, return_all_stdout: false, output_separator: nil) ⇒ Object
execute a command with real-time output.
- .levels ⇒ Object
- .logger ⇒ Object
-
.logger=(value) ⇒ Object
For portability, can be overridden with a class that has methods :level, :fatal, :error, :warn, :info, :debug and the others specified below.
- .notepad_prompt(text_file_path, comment) ⇒ Object
- .pid_running?(pid) ⇒ Boolean
-
.powershell_out(ps_script, pid_logfile: nil, working_folder: Dir.pwd, regex_error_filters: [], info_on_exception: '', exception_exceptions: [], show_command_on_error: false, return_all_stdout: false, output_separator: nil) ⇒ Object
execute a powershell script with real-time output.
- .run_command_on_remote_hosts(remote_hosts, command, credentials, command_message: nil, shell_type: :cmd, tail_count: nil, set_as_trusted_host: false) ⇒ Object
- .run_remote_powershell_command(remote_host, command, credentials, set_as_trusted_host: false) ⇒ Object
Class Method Details
._full_error_message(error_message, error_options, command) ⇒ Object
142 143 144 145 |
# File 'lib/easy_io/run.rb', line 142 def (, , command) = ['show_command_on_error'] && command ? "\nCommand causing exception: " + command + "\n" : '' "Exception: #{error_message}\n#{error_options['info_on_exception']}#{'=' * 120}\n#{command_message}" end |
._parse_for_errors(message, error_messages, error_options, command) ⇒ Object
132 133 134 135 |
# File 'lib/easy_io/run.rb', line 132 def _parse_for_errors(, , , command) errors_found = ['regex_error_filters'].any? { |regex_filter| =~ regex_filter } (, , , command) if errors_found end |
._process_error_message(error_message, error_messages, error_options, command) ⇒ Object
137 138 139 140 |
# File 'lib/easy_io/run.rb', line 137 def (, , , command) raise (, , command) if ['raise_on_first_error'] .push() # if we're not raising right away, add to the list of errors end |
.add_as_winrm_trusted_host(remote_host) ⇒ Object
127 128 129 130 |
# File 'lib/easy_io/run.rb', line 127 def add_as_winrm_trusted_host(remote_host) trusted_hosts = EasyIO.powershell_out('(Get-Item WSMan:\localhost\Client\TrustedHosts).value', return_all_stdout: true) EasyIO.powershell_out("Set-Item WSMan:\\localhost\\Client\\TrustedHosts -Value 'trusted_hosts, #{remote_host}' -Force") unless trusted_hosts.include?(remote_host) end |
.config ⇒ Object
4 5 6 |
# File 'lib/easy_io/config.rb', line 4 def config @config ||= EasyJSON.config(defaults: defaults) end |
.defaults ⇒ Object
8 9 10 11 12 13 14 |
# File 'lib/easy_io/config.rb', line 8 def defaults { 'paths' => { 'cache' => Dir.tmpdir, }, } end |
.execute_out(command, pid_logfile: nil, working_folder: Dir.pwd, regex_error_filters: [], info_on_exception: '', exception_exceptions: [], powershell: false, show_command_on_error: false, raise_on_first_error: true, return_all_stdout: false, output_separator: nil) ⇒ Object
execute a command with real-time output. Any stdout you want returned to the caller must come after the :output_separator which defaults to ‘#return_data#:’
return_all_stdout: return all output to the caller instead after process completion
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/easy_io/run.rb', line 6 def execute_out(command, pid_logfile: nil, working_folder: Dir.pwd, regex_error_filters: [], info_on_exception: '', exception_exceptions: [], powershell: false, show_command_on_error: false, raise_on_first_error: true, return_all_stdout: false, output_separator: nil) output_separator ||= '#return_data#:' if return_all_stdout result = '' return_data_flag = true else STDOUT.sync = true result = nil return_data_flag = false end exit_status = nil = [] info_on_exception = "#{info_on_exception}\n" unless info_on_exception.end_with?("\n") = { 'show_command_on_error' => show_command_on_error, 'info_on_exception' => info_on_exception, 'regex_error_filters' => regex_error_filters, 'raise_on_first_error' => raise_on_first_error } if powershell ps_script_file = "#{EasyIO.config['paths']['cache']}/easy_io/scripts/ps_script-thread_id-#{Thread.current.object_id}.ps1" FileUtils.mkdir_p(::File.dirname(ps_script_file)) unless ::File.directory? ::File.dirname(ps_script_file) ::File.write(ps_script_file, command) end popen_arguments = powershell ? ['powershell.exe', ps_script_file] : [command] Open3.popen3(*popen_arguments, chdir: working_folder) do |_stdin, stdout, stderr, wait_thread| unless pid_logfile.nil? # Log pid in case job or script dies FileUtils.mkdir_p(::File.dirname(pid_logfile)) unless ::File.directory? ::File.dirname(pid_logfile) ::File.write(pid_logfile, wait_thread.pid) end buffers = [stdout, stderr] queued_buffers = IO.select(buffers) || [[]] queued_buffers.first.each do |buffer| case buffer when stdout while (line = buffer.gets) if return_data_flag result += line next end stdout_split = line.split(output_separator) = stdout_split.first.strip _parse_for_errors(, , , command) EasyIO.logger.info unless .empty? if stdout_split.count > 1 return_data_flag = true result = stdout_split.last end end when stderr = '' += line while (line = buffer.gets) next if .empty? if exception_exceptions.any? { |ignore_filter| =~ ignore_filter } EasyIO.logger.info .strip next end (, , , command) end end exit_status = wait_thread.value end unless .empty? last_error = (.pop, , command) .map! { || (, , nil) } .push(last_error) raise .join("\n") end [result, exit_status] end |
.levels ⇒ Object
44 45 46 47 48 49 50 51 52 53 |
# File 'lib/easy_io/logger.rb', line 44 def levels { 'info' => Logger::INFO, 'debug' => Logger::DEBUG, 'warn' => Logger::WARN, 'error' => Logger::ERROR, 'fatal' => Logger::FATAL, 'unknown' => Logger::UNKNOWN, } end |
.logger ⇒ Object
40 41 42 |
# File 'lib/easy_io/logger.rb', line 40 def logger @logger end |
.logger=(value) ⇒ Object
For portability, can be overridden with a class that has methods :level, :fatal, :error, :warn, :info, :debug and the others specified below. See ruby-doc.org/stdlib-2.4.0/libdoc/logger/rdoc/Logger.html
For example, when using with Chef, set the logger to Chef::Log
35 36 37 38 |
# File 'lib/easy_io/logger.rb', line 35 def logger=(value) @logger = value @logger.class.class_eval { include LoggerEnhancement } end |
.notepad_prompt(text_file_path, comment) ⇒ Object
155 156 157 158 159 160 161 162 |
# File 'lib/easy_io/run.rb', line 155 def notepad_prompt(text_file_path, comment) ::FileUtils.mkdir_p ::File.dirname(text_file_path) unless ::File.directory?(::File.dirname(text_file_path)) ::File.write(text_file_path, "; #{comment}") unless ::File.exist?(text_file_path) EasyIO.logger.info comment.gsub('here', 'in the notepad window') `notepad #{text_file_path}` notepad_content = ::File.read(text_file_path) notepad_content.gsub(/;[^\r\n]*(\r\n|\r|\n)/i, '') # remove comments in text file end |
.pid_running?(pid) ⇒ Boolean
147 148 149 150 151 152 153 |
# File 'lib/easy_io/run.rb', line 147 def pid_running?(pid) begin Process.kill(0, pid) # Does not actually kill process, checks if it's running. rescue Errno::ESRCH nil end == 1 end |
.powershell_out(ps_script, pid_logfile: nil, working_folder: Dir.pwd, regex_error_filters: [], info_on_exception: '', exception_exceptions: [], show_command_on_error: false, return_all_stdout: false, output_separator: nil) ⇒ Object
execute a powershell script with real-time output. Any stdout you want returned to the caller must come after the :output_separator which defaults to ‘#return_data#:’
return_all_stdout: return all output to the caller instead after process completion
74 75 76 |
# File 'lib/easy_io/run.rb', line 74 def powershell_out(ps_script, pid_logfile: nil, working_folder: Dir.pwd, regex_error_filters: [], info_on_exception: '', exception_exceptions: [], show_command_on_error: false, return_all_stdout: false, output_separator: nil) execute_out(ps_script, pid_logfile: pid_logfile, working_folder: working_folder, regex_error_filters: regex_error_filters, info_on_exception: info_on_exception, exception_exceptions: exception_exceptions, powershell: true, show_command_on_error: show_command_on_error, return_all_stdout: return_all_stdout, output_separator: output_separator) end |
.run_command_on_remote_hosts(remote_hosts, command, credentials, command_message: nil, shell_type: :cmd, tail_count: nil, set_as_trusted_host: false) ⇒ Object
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/easy_io/run.rb', line 99 def run_command_on_remote_hosts(remote_hosts, command, credentials, command_message: nil, shell_type: :cmd, tail_count: nil, set_as_trusted_host: false) tail_count ||= 1 # Return the last (1) line from each remote_host's log to the console supported_shell_types = [:cmd, :powershell] # TODO: implement shell_type :bash raise "Unsupported shell_type for running remote commands: '#{shell_type}'" unless supported_shell_types.include?(shell_type) threads = {} threads_output = {} log_folder = "#{EasyIO.config['paths']['cache']}/easy_io/logs" ::FileUtils.mkdir_p log_folder unless ::File.directory?(log_folder) EasyIO.logger.info "Output logs of processes run on the specified remote hosts will be placed in #{log_folder}..." remote_hosts.each do |remote_host| EasyIO.logger.info "Running `#{command_message || command}` on #{remote_host}..." # threads_output[remote_host] = run_remote_powershell_command(remote_host, command, credentials, set_as_trusted_host: set_as_trusted_host) threads[remote_host] = Thread.new do threads_output[remote_host] = run_remote_powershell_command(remote_host, command, credentials, set_as_trusted_host: set_as_trusted_host) end end threads.values.each(&:join) # Wait for all commands to complete # threads.each { |remote_host, thread| pp thread } threads_output.each do |remote_host, output| ::File.write("#{log_folder}/#{EasyFormat::File.windows_friendly_name(remote_host)}.#{::Time.now.strftime('%Y%m%d_%H%M%S')}.log", "#{output['stdout']}\n#{output['stderr']}") tail_output = output['stdout'].nil? ? '--no standard output--' : output['stdout'].split("\n").last(tail_count).join("\n") EasyIO.logger.info "[#{remote_host}]: #{tail_output}" raise "Failed to run command on #{remote_host}: #{output['stderr']}\n#{output['exception'].cause}\n#{output['exception'].message}" if output['exception'] raise "The script exited with exit code #{output['exitcode']}.\n\n#{output['stderr']}" unless output['exitcode'] == 0 end end |
.run_remote_powershell_command(remote_host, command, credentials, set_as_trusted_host: false) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/easy_io/run.rb', line 78 def run_remote_powershell_command(remote_host, command, credentials, set_as_trusted_host: false) add_as_winrm_trusted_host(remote_host) if set_as_trusted_host remote_command = "$securePassword = ConvertTo-SecureString -AsPlainText '\#{credentials['password']}' -Force\n$credential = New-Object System.Management.Automation.PSCredential -ArgumentList \#{credentials['user']}, $securePassword\nInvoke-Command -ComputerName \#{remote_host} -Credential $credential -ScriptBlock { \#{command} }\n" output = powershell_out(remote_command, return_all_stdout: true) { 'stdout' => output.first, 'exitcode' => output.last, } rescue => ex { 'exception' => ex, 'stderr' => ex., 'exitcode' => 1, } end |