Class: WinRM::CommandExecutor

Inherits:
Object
  • Object
show all
Defined in:
lib/winrm/command_executor.rb

Overview

Object which can execute multiple commands and Powershell scripts in one shared remote shell session. The maximum number of commands per shell is determined by interrogating the remote host when the session is opened and the remote shell is automatically recycled before the threshold is reached.

Author:

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(service) ⇒ CommandExecutor

Creates a CommandExecutor given a ‘WinRM::WinRMWebService` object.

Parameters:

  • service (WinRM::WinRMWebService)

    a winrm web service object responds to ‘#debug` and `#info` (default: `nil`)



49
50
51
52
# File 'lib/winrm/command_executor.rb', line 49

def initialize(service)
  @service = service
  @command_count = 0
end

Instance Attribute Details

#serviceWinRM::WinRMWebService (readonly)

Returns a WinRM web service object.

Returns:



39
40
41
# File 'lib/winrm/command_executor.rb', line 39

def service
  @service
end

#shellString? (readonly)

Returns the identifier for the current open remote shell session, or ‘nil` if the session is not open.

Returns:

  • (String, nil)

    the identifier for the current open remote shell session, or ‘nil` if the session is not open



43
44
45
# File 'lib/winrm/command_executor.rb', line 43

def shell
  @shell
end

Class Method Details

.finalize(shell_id, service) ⇒ Object

Closes an open remote shell session left open after a command executor is garbage collecyted.

Parameters:

  • shell_id (String)

    the remote shell identifier

  • service (WinRM::WinRMWebService)

    a winrm web service object



34
35
36
# File 'lib/winrm/command_executor.rb', line 34

def self.finalize(shell_id, service)
  proc { service.close_shell(shell_id) }
end

Instance Method Details

#closeObject

Closes the open remote shell session. This method can be called multiple times, even if there is no open session.



56
57
58
59
60
61
62
# File 'lib/winrm/command_executor.rb', line 56

def close
  return if shell.nil?

  service.close_shell(shell)
  remove_finalizer
  @shell = nil
end

#code_pageInteger

Code page appropriate to os version. utf-8 (65001) is buggy pre win7/2k8r2 So send MS-DOS (437) for earlier versions

Returns:

  • (Integer)

    code page in use



148
149
150
# File 'lib/winrm/command_executor.rb', line 148

def code_page
  @code_page ||= os_version < Gem::Version.new('6.1') ? 437 : 65_001
end

#max_commandsInteger

Returns the safe maximum number of commands that can be executed in one remote shell session.

Returns:

  • (Integer)

    the safe maximum number of commands that can be executed in one remote shell session



154
155
156
# File 'lib/winrm/command_executor.rb', line 154

def max_commands
  @max_commands ||= (os_version < Gem::Version.new('6.2') ? 15 : 1500) - 2
end

#openString

Opens a remote shell session for reuse. The maxiumum command-per-shell threshold is also determined the first time this method is invoked and cached for later invocations.

Returns:

  • (String)

    the remote shell session indentifier



69
70
71
72
73
74
75
76
77
# File 'lib/winrm/command_executor.rb', line 69

def open
  close
  retryable(service.retry_limit, service.retry_delay) do
    @shell = service.open_shell(codepage: code_page)
  end
  add_finalizer(shell)
  @command_count = 0
  shell
end

#run_cmd(command, arguments = []) {|stdout, stderr| ... } ⇒ WinRM::Output

Runs a CMD command.

Parameters:

  • command (String)

    the command to run on the remote system

  • arguments (Array<String>) (defaults to: [])

    arguments to the command

Yields:

  • (stdout, stderr)

    yields more live access the standard output and standard error streams as they are returns, if streaming behavior is desired

Returns:

  • (WinRM::Output)

    output object with stdout, stderr, and exit code



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/winrm/command_executor.rb', line 88

def run_cmd(command, arguments = [], &block)
  tries ||= 2

  reset if command_count > max_commands
  ensure_open_shell!

  @command_count += 1
  result = nil
  service.run_command(shell, command, arguments) do |command_id|
    result = service.get_command_output(shell, command_id, &block)
  end
  result
rescue WinRMWSManFault => e
  # Fault code 2150858843 may be raised if the shell id that the command is sent on
  # has been closed. One might see this on a target windows machine that has just
  # been provisioned and restarted the winrm service at the end of its provisioning
  # routine. If this hapens, we should give the command one more try.

  # Fault code 2147943418 may be raised if the shell is installing an msi and for
  # some reason it tries to read a registry key that has been deleted. This sometimes
  # happens for 2008r2 when installing the Chef omnibus msi.
  if %w(2150858843 2147943418).include?(e.fault_code) && (tries -= 1) > 0
    service.logger.debug('[WinRM] opening new shell since the current one failed')
    @shell = nil
    open
    retry
  else
    raise
  end
end

#run_powershell_script(script_file) {|stdout, stderr| ... } ⇒ WinRM::Output

Run a Powershell script that resides on the local box.

Parameters:

  • script_file (IO, String)

    an IO reference for reading the Powershell script or the actual file contents

Yields:

  • (stdout, stderr)

    yields more live access the standard output and standard error streams as they are returns, if streaming behavior is desired

Returns:

  • (WinRM::Output)

    output object with stdout, stderr, and exit code



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/winrm/command_executor.rb', line 128

def run_powershell_script(script_file, &block)
  # this code looks overly compact in an attempt to limit local
  # variable assignments that may contain large strings and
  # consequently bloat the Ruby VM
  run_cmd(
    'powershell',
    [
      '-encodedCommand',
      ::WinRM::PowershellScript.new(
        script_file.is_a?(IO) ? script_file.read : script_file
      ).encoded
    ],
    &block
  )
end