Parent

Files

WinRM::WinRMWebService

This is the main class that does the SOAP request/response logic. There are a few helper classes, but pretty

much everything comes through here first.

Attributes

endpoint[R]
timeout[R]

Public Class Methods

new(endpoint, transport = :kerberos, opts = {}) click to toggle source

@param [String,URI] endpoint the WinRM webservice endpoint @param [Symbol] transport either :kerberos(default)/:ssl/:plaintext @param [Hash] opts Misc opts for the various transports.

@see WinRM::HTTP::HttpTransport
@see WinRM::HTTP::HttpGSSAPI
@see WinRM::HTTP::HttpSSL
# File lib/winrm/winrm_service.rb, line 36
def initialize(endpoint, transport = :kerberos, opts = {})
  @endpoint = endpoint
  @timeout = DEFAULT_TIMEOUT
  @max_env_sz = DEFAULT_MAX_ENV_SIZE 
  @locale = DEFAULT_LOCALE
  case transport
  when :kerberos
    require 'gssapi'
    # TODO: check fo keys and throw error if missing
    @xfer = HTTP::HttpGSSAPI.new(endpoint, opts[:realm], opts[:service], opts[:keytab], opts)
  when :plaintext
    @xfer = HTTP::HttpPlaintext.new(endpoint, opts[:user], opts[:pass], opts)
  when :ssl
    @xfer = HTTP::HttpSSL.new(endpoint, opts[:user], opts[:pass], opts[:ca_trust_path], opts)
  end
end

Public Instance Methods

cleanup_command(shell_id, command_id) click to toggle source

Clean-up after a command. @see run_command @param [String] shell_id The shell id on the remote machine. See open_shell @param [String] command_id The command id on the remote machine. See run_command @return [true] This should have more error checking but it just returns true for now.

# File lib/winrm/winrm_service.rb, line 188
def cleanup_command(shell_id, command_id)
  s = Savon::SOAP::XML.new
  s.version = 2
  s.namespaces.merge!(namespaces)
  s.header.merge!(merge_headers(header,resource_uri_cmd,action_signal,selector_shell_id(shell_id)))

  # Signal the Command references to terminate (close stdout/stderr)
  s.input = ["#{NS_WIN_SHELL}:Signal", {'CommandId' => command_id}]

  s.body = { "#{NS_WIN_SHELL}:Code" => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate' }
  resp = send_message(s.to_xml)
  true
end
close_shell(shell_id) click to toggle source

Close the shell @param [String] shell_id The shell id on the remote machine. See open_shell @return [true] This should have more error checking but it just returns true for now.

# File lib/winrm/winrm_service.rb, line 205
def close_shell(shell_id)
  s = Savon::SOAP::XML.new
  s.version = 2
  s.namespaces.merge!(namespaces)
  s.namespaces.merge!(Savon::SOAP::XML::SchemaTypes)
  s.header.merge!(merge_headers(header,resource_uri_cmd,action_delete,selector_shell_id(shell_id)))

  # Because Savon does not support a nil Body we have to build it ourselves.
  s.xml do |b|
    b.tag!('env:Envelope', s.namespaces) do
      b.tag!('env:Header') do |bh|
        bh << Gyoku.xml(s.header) unless s.header.empty?
      end
      if(s.input.nil?)
        b.tag! 'env:Body'
      else
        b.tag! 'env:Body' do |bb|
          bb.tag! s.input do |bbb|
            bbb << Gyoku.xml(s.body) if s.body
          end
        end
      end
    end
  end

  resp = send_message(s.to_xml)
  true
end
cmd(command, arguments = [], &block) click to toggle source
Alias for: run_cmd
get_command_output(shell_id, command_id, &block) click to toggle source

Get the Output of the given shell and command @param [String] shell_id The shell id on the remote machine. See open_shell @param [String] command_id The command id on the remote machine. See run_command @return [Hash] Returns a Hash with a key :exitcode and :data. Data is an Array of Hashes where the cooresponding key

is either :stdout or :stderr.  The reason it is in an Array so so we can get the output in the order it ocurrs on
the console.
# File lib/winrm/winrm_service.rb, line 146
def get_command_output(shell_id, command_id, &block)
  s = Savon::SOAP::XML.new
  s.version = 2
  s.namespaces.merge!(namespaces)
  s.header.merge!(merge_headers(header,resource_uri_cmd,action_receive,selector_shell_id(shell_id)))
  s.input = "#{NS_WIN_SHELL}:Receive"
  s.body = { "#{NS_WIN_SHELL}:DesiredStream" => 'stdout stderr',
    :attributes! => {"#{NS_WIN_SHELL}:DesiredStream" => {'CommandId' => command_id}}}

  resp = send_message(s.to_xml)
  output = {:data => []}
  (resp/"//#{NS_WIN_SHELL}:Stream").each do |n|
    next if n.text.nil? || n.text.empty?
    stream = {n['Name'].to_sym => Base64.decode64(n.text)}
    output[:data] << stream
    yield stream[:stdout], stream[:stderr] if block_given?
  end

  # We may need to get additional output if the stream has not finished.
  # The CommandState will change from Running to Done like so:
  # @example
  #   from...
  #   <rsp:CommandState CommandId="..." State="http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Running"/>
  #   to...
  #   <rsp:CommandState CommandId="..." State="http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Done">
  #     <rsp:ExitCode>0</rsp:ExitCode>
  #   </rsp:CommandState>
  if((resp/"//*[@State='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Done']").empty?)
    output.merge!(get_command_output(shell_id,command_id,&block)) do |key, old_data, new_data|
      old_data += new_data
    end
  else
    output[:exitcode] = (resp/"//#{NS_WIN_SHELL}:ExitCode").text.to_i
  end
  output
end
locale(locale) click to toggle source

Set the locale @see msdn.microsoft.com/en-us/library/gg567404(v=PROT.13).aspx @param [String] locale the locale to set for future messages

# File lib/winrm/winrm_service.rb, line 71
def locale(locale)
  @locale = locale
end
max_env_size(byte_sz) click to toggle source

Max envelope size @see msdn.microsoft.com/en-us/library/ee916127(v=PROT.13).aspx @param [Fixnum] byte_sz the max size in bytes to allow for the response

# File lib/winrm/winrm_service.rb, line 64
def max_env_size(byte_sz)
  @max_env_sz = byte_sz
end
op_timeout(sec) click to toggle source
Alias for: set_timeout
open_shell(shell_opts = {}) click to toggle source

Create a Shell on the destination host @param [Hash<optional>] shell_opts additional shell options you can pass @option shell_opts [String] :i_stream Which input stream to open. Leave this alone unless you know what you’re doing (default: stdin) @option shell_opts [String] :o_stream Which output stream to open. Leave this alone unless you know what you’re doing (default: stdout stderr) @option shell_opts [String] :working_directory the directory to create the shell in @option shell_opts [Hash] :env_vars environment variables to set for the shell. For instance;

:env_vars => {:myvar1 => 'val1', :myvar2 => 'var2'}

@return [String] The ShellId from the SOAP response. This is our open shell instance on the remote machine.

# File lib/winrm/winrm_service.rb, line 83
def open_shell(shell_opts = {})
  i_stream = shell_opts.has_key?(:i_stream) ? shell_opts[:i_stream] : 'stdin'
  o_stream = shell_opts.has_key?(:o_stream) ? shell_opts[:o_stream] : 'stdout stderr'
  codepage = shell_opts.has_key?(:codepage) ? shell_opts[:codepage] : 437
  noprofile = shell_opts.has_key?(:noprofile) ? shell_opts[:noprofile] : 'FALSE'
  s = Savon::SOAP::XML.new
  s.version = 2
  s.namespaces.merge!(namespaces)
  h_opts = { "#{NS_WSMAN_DMTF}:OptionSet" => { "#{NS_WSMAN_DMTF}:Option" => [noprofile, codepage],
    :attributes! => {"#{NS_WSMAN_DMTF}:Option" => {'Name' => ['WINRS_NOPROFILE','WINRS_CODEPAGE']}}}}
  s.header.merge!(merge_headers(header,resource_uri_cmd,action_create,h_opts))
  s.input = "#{NS_WIN_SHELL}:Shell"
  s.body = {
    "#{NS_WIN_SHELL}:InputStreams" => i_stream,
    "#{NS_WIN_SHELL}:OutputStreams" => o_stream
  }
  s.body["#{NS_WIN_SHELL}:WorkingDirectory"] = shell_opts[:working_directory] if shell_opts.has_key?(:working_directory)
  # TODO: research Lifetime a bit more: http://msdn.microsoft.com/en-us/library/cc251546(v=PROT.13).aspx
  #s.body["#{NS_WIN_SHELL}:Lifetime"] = Iso8601Duration.sec_to_dur(shell_opts[:lifetime]) if(shell_opts.has_key?(:lifetime) && shell_opts[:lifetime].is_a?(Fixnum))
  # @todo make it so the input is given in milliseconds and converted to xs:duration
  s.body["#{NS_WIN_SHELL}:IdleTimeOut"] = shell_opts[:idle_timeout] if(shell_opts.has_key?(:idle_timeout) && shell_opts[:idle_timeout].is_a?(String))
  if(shell_opts.has_key?(:env_vars) && shell_opts[:env_vars].is_a?(Hash))
    keys = shell_opts[:env_vars].keys
    vals = shell_opts[:env_vars].values
    s.body["#{NS_WIN_SHELL}:Environment"] = {
      "#{NS_WIN_SHELL}:Variable" => vals,
      :attributes! => {"#{NS_WIN_SHELL}:Variable" => {'Name' => keys}}
    }
  end

  resp = send_message(s.to_xml)
  (resp/"//*[@Name='ShellId']").text
end
powershell(script_file, &block) click to toggle source
run_cmd(command, arguments = [], &block) click to toggle source

Run a CMD command @param [String] command The command to run on the remote system @param [Array <String>] arguments arguments to the command @return [Hash] :stdout and :stderr

# File lib/winrm/winrm_service.rb, line 238
def run_cmd(command, arguments = [], &block)
  shell_id = open_shell
  command_id =  run_command(shell_id, command, arguments)
  command_output = get_command_output(shell_id, command_id, &block)
  cleanup_command(shell_id, command_id)
  close_shell(shell_id)
  command_output
end
Also aliased as: cmd
run_command(shell_id, command, arguments = [], cmd_opts = {}) click to toggle source

Run a command on a machine with an open shell @param [String] shell_id The shell id on the remote machine. See open_shell @param [String] command The command to run on the remote machine @param [Array<String>] arguments An array of arguments for this command @return [String] The CommandId from the SOAP response. This is the ID we need to query in order to get output.

# File lib/winrm/winrm_service.rb, line 122
def run_command(shell_id, command, arguments = [], cmd_opts = {})
  consolemode = cmd_opts.has_key?(:console_mode_stdin) ? cmd_opts[:console_mode_stdin] : 'TRUE'
  skipcmd     = cmd_opts.has_key?(:skip_cmd_shell) ? cmd_opts[:skip_cmd_shell] : 'FALSE'
  s = Savon::SOAP::XML.new
  s.version = 2
  s.namespaces.merge!(namespaces)
  h_opts = { "#{NS_WSMAN_DMTF}:OptionSet" => {
    "#{NS_WSMAN_DMTF}:Option" => [consolemode, skipcmd],
    :attributes! => {"#{NS_WSMAN_DMTF}:Option" => {'Name' => ['WINRS_CONSOLEMODE_STDIN','WINRS_SKIP_CMD_SHELL']}}}
  }
  s.header.merge!(merge_headers(header,resource_uri_cmd,action_command,h_opts,selector_shell_id(shell_id)))
  s.input = "#{NS_WIN_SHELL}:CommandLine"
  s.body = { "#{NS_WIN_SHELL}:Command" => "\"#{command}\"", "#{NS_WIN_SHELL}:Arguments" => arguments}

  resp = send_message(s.to_xml)
  (resp/"//#{NS_WIN_SHELL}:CommandId").text
end
run_powershell_script(script_file, &block) click to toggle source

Run a Powershell script that resides on the local box. @param [IO,String] script_file an IO reference for reading the Powershell script or the actual file contents @return [Hash] :stdout and :stderr

# File lib/winrm/winrm_service.rb, line 252
def run_powershell_script(script_file, &block)
  # if an IO object is passed read it..otherwise assume the contents of the file were passed
  script = script_file.kind_of?(IO) ? script_file.read : script_file

  script = script.chars.to_a.join("\x00").chomp
  script << "\x00" unless script[-1].eql? "\x00"
  if(defined?(script.encode))
    script = script.encode('ASCII-8BIT')
    script = Base64.strict_encode64(script)
  else
    script = Base64.encode64(script).chomp
  end

  shell_id = open_shell
  command_id = run_command(shell_id, "powershell -encodedCommand #{script}")
  command_output = get_command_output(shell_id, command_id, &block)
  cleanup_command(shell_id, command_id)
  close_shell(shell_id)
  command_output
end
Also aliased as: powershell
run_wql(wql) click to toggle source

Run a WQL Query @see msdn.microsoft.com/en-us/library/aa394606(VS.85).aspx @param [String] wql The WQL query @return [Hash] Returns a Hash that contain the key/value pairs returned from the query.

# File lib/winrm/winrm_service.rb, line 279
def run_wql(wql)
  s = Savon::SOAP::XML.new
  s.version = 2
  s.namespaces.merge!(namespaces)
  s.header.merge!(merge_headers(header,resource_uri_wmi,action_enumerate))
  s.input = "#{NS_ENUM}:Enumerate"
  s.body = { "#{NS_WSMAN_DMTF}:OptimizeEnumeration" => nil,
    "#{NS_WSMAN_DMTF}:MaxElements" => '32000',
    "#{NS_WSMAN_DMTF}:Filter" => wql,
    :attributes! => { "#{NS_WSMAN_DMTF}:Filter" => {'Dialect' => 'http://schemas.microsoft.com/wbem/wsman/1/WQL'}}
  }

  resp = send_message(s.to_xml)
  toggle_nori_type_casting :off
  hresp = Nori.parse(resp.to_xml)[:envelope][:body]
  toggle_nori_type_casting :original
  # Normalize items so the type always has an array even if it's just a single item.
  items = {}
  hresp[:enumerate_response][:items].each_pair do |k,v|
    if v.is_a?(Array)
      items[k] = v
    else
      items[k] = [v]
    end
  end
  items
end
Also aliased as: wql
set_timeout(sec) click to toggle source

Operation timeout @see msdn.microsoft.com/en-us/library/ee916629(v=PROT.13).aspx @param [Fixnum] sec the number of seconds to set the timeout to. It will be converted to an ISO8601 format.

# File lib/winrm/winrm_service.rb, line 56
def set_timeout(sec)
  @timeout = Iso8601Duration.sec_to_dur(sec)
end
Also aliased as: op_timeout
toggle_nori_type_casting(to) click to toggle source
# File lib/winrm/winrm_service.rb, line 308
def toggle_nori_type_casting(to)
  @nori_type_casting ||= Nori.advanced_typecasting?
  case to.to_sym
  when :original
    Nori.advanced_typecasting = @nori_type_casting
  when :on
    Nori.advanced_typecasting = true
  when :off
    Nori.advanced_typecasting = false
  else
    raise ArgumentError, "Cannot toggle type casting to '#{to}', it is not a valid argument"
  end

end
wql(wql) click to toggle source
Alias for: run_wql

[Validate]

Generated with the Darkfish Rdoc Generator 2.