Limepoint Automation RPC Utils

Eventually this will evolve into a full set of RPC utilities, but for now:

  • Pluggable transports, though only SSH is implemented

  • Support for hopping through jump hosts

  • Sudo with password/nopassword

  • Keys or password auth

  • Does not rely on Port Forwarding, clean terminals, or so on, so should work on any weird enterprise network

  • Reuses existing connections to avoid lots of connectivity latency

Provides * simple shell execution * more complex shell execution with wait/inject functions * file transfers * remote ruby code execution, including shipping a transient interpreter * access to files as ruby objects

Environment Variables

  • LAS_PROMPT_TIMEOUT - timeout for how long to wait for a prompt. Default is 300s, to allow for slow sudo/ldap configurations

  • LAS_READY_TIMEOUT - the time between executing a script, and seeing the “script started” output. Default is 30s.

  • LAS_WAIT_TIMEOUT - the time to wait at the end of the script. defualt is 30s.

  • LAS_DISABLE_TRANSFORM - dont use a microservice for reconnectable connections, but instead use a pure SSH connection

Getting static ruby to endpoints

How this happens is controlled by the environment variable LAS_RUBY_METHOD - currnetly:

  • gem - push from the gem via SSH (this is the default)

  • http - grab from limepoint’s public HTTP server

  • any url starting with ‘http’ - grab from that URL specifically

Use

Usually, for the general case, you use this through a transport manager - so you end up with code like:

transport_options = {}
      if @sudo
        transport_options[:user] = @sudo_user
        transport_options[:password] = @sudo_password
        transport_options[:keys] = [@ssh_key]
        transport_options[:sudo_user] = @ssh_username
        transport_options[:port] = @ssh_port
      else
        transport_options[:user] = @ssh_username
        transport_options[:password] = @ssh_password
        transport_options[:keys] = [@ssh_key]
        transport_options[:port] = @ssh_port
      end
$transport_factory = ASRpcUtils::TransportFactory.new(default_type: 'ssh', default_transport_options: transport_options)
#Also now set_options_for_host to do per host options
$transport_factory.set_options_for_host('jjosi1', {user: jj})
# This takes a string
$transport_factory.add_default_path("/oracle/product/bin")
# THis takes a hash
$transport_factory.add_default_env({"ORACLE_HOME" => "/oracle/product")
conn = $transport_factory.transport(hostname)
reply=conn.excute("echo $PATH")
puts reply

For now, see samples in test/ folder, but:

TransportManager

  • See above - JJ needs to document this more! But usually, call .transport to get a transport once set up

  • Usually you want to set environments on this, unless you really specifically want it on your connection object

Transport

  • LASRpcUtils::Transport.new(host, type, transport_options) Initiate a transport Example: LASRpcUtils::Transport.new('testosi1.mintpress.io', 'ssh', { sudo_user: 'oracle', user: 'root', keys: [ '~/.ssh/id_rsa' ]})

  • transport.add_hop Add a hop to a transport

  • transport.alive? Test if a transport is alive

  • transport.execute(command) Execute a remote command. Returns a triple of return_code,stdout,stderr Example: transport.execute("whoami")

  • transport.content(filename) Return the content of a file Example: transport.content("/etc/hosts")

  • transport.getfile(local,remote)

  • transport.putfile(local,remote)

  • transport.file_exists?(filename) Boolean if a file/folder exists

  • transport.ruby(&block) Execute ruby Example:

    filename="/etc/hosts"
       content=transport.ruby do 
     File.read(filename)
       end
    

The ruby transport will get the contents of all marshalable local variables. Native ruby types will be fine, however un-marshalable objects will NOT be transported - obviously, anything with IO is the big gotcha here. * transport.port_forward_via_local(listen_address_on_transport, listen_port_on_transport, destination_address, destination_port) Sets up a port forward. example: transport.port_forward_via_local(“localhost”, 443, “chef-server.mintpress.io”, 443)

  • transport.File Return a remote ruby file object Example: transport.File.open("/etc/hosts").read()

  • transport.ruby_require(thing) Require a module in the rmeote ruby session

  • transport.ruby_include(thing) Include a namespace in the remote ruby sessioon

  • transport.wrap_object(obj) Add a wrapped object - it appears as a function of transport just like the below objects

    transport.ruby_require("rexml/document")
    transport.ruby_include("REXML")
    transport.wrap_object("Document")
    doc=transport.Document.new "/environmint/runtime/data/MintPress/bamboo.cfg.xml"
    puts doc.class
    doc.root.attributes.each do |e|
      puts "ele: #{e}"
    end
    
  • transport.Dir

  • transport.IO

  • transport.TcpSocket

  • transport.UdpSocket

  • transport.Socket

  • transport.HTTPClient

  • transport.RestClient

  • transport.OCI8

  • transport.Tempfile Return remote ruby objects If you need more of these, add them to $wrapped_remote_objects in transport.rb!

Note that there are some limitations due to the way the wrapping works - the most notable being that, while blocks work normally for File/HTTP objects, they do NOT work normally for OCI8 objects, since it uses block detection to behave differently. For objects like this, you should use a ruby block instead - i.e.

transport.ruby do
  db=OCI8.new('sys', 'welcome1', '//uomdb1.mintpress.io/ORADB', :SYSDBA)
  db.exec("select * from dba_users") do |r|
    puts r.join(',')
  end
end

will work, whereas:

db=transport.OCI8.new('sys', 'welcome1', '//uomdb1.mintpress.io/ORADB', :SYSDBA)
db.exec("select * from dba_users") do |r|
  puts r.join(',')
end

Will throw an exception because the object does not take a block

  • transport.start_execute(command) start a command runnning on an interactive channel

  • channel.send_text(text) send text to the channel

  • channel.wait_for_stdout(wtext, type: 'normal', failtext: nil, timeout: 3600, trim_working: false, local_echo: true, filter: ['---limepoint-']) Wait for stdout to do something. Accepts:

  • wtext - array of matches

  • type - match type. normal, fnmatch, or regex

  • failtext - if this match is hit, fail immediatly

  • timeout - how long to wait

  • trim_working - trim the log. this is generally only for internal use

  • local_echo - log as we wait

  • filter - items to not show in the logs - use this for escapes and such

  • channel.wait_for_complete Wait for your channel to complete. Returns the triplet (returncode, stdout, stderr)

License & Authors

  • Author:: LimePoint (support@limepoint.com)

# MintPress® - Automation and Configuration Management
#
# Copyright © 2014 LimePoint. All rights reserved.
#
# This program and its contents are confidential and owned by LimePoint.
# Only licenced users are permitted to access and use of this file.
# This program (or any part of it) may not be disclosed, copied or used
# except as expressly permitted in LimePoint’s End User Licence Agreement.
#
# LimePoint® and MintPress® are Registered Trademarks of LimePoint IP Limited.
# For more information contact LimePoint at http://www.limepoint.com