lib/symphony/tasks/sshscript.rb
changeset 19 f31d60b04f8a
parent 11 ffa70066522c
equal deleted inserted replaced
18:d629b9939df4 19:f31d60b04f8a
    25 ###    port:       (optional) The port to connect to (defaults to 22)
    25 ###    port:       (optional) The port to connect to (defaults to 22)
    26 ###    user:       (optional) The user to connect as (defaults to root)
    26 ###    user:       (optional) The user to connect as (defaults to root)
    27 ###    key:        (optional) The path to an SSH private key
    27 ###    key:        (optional) The path to an SSH private key
    28 ###    attributes: (optional) Additional data to attach to the template
    28 ###    attributes: (optional) Additional data to attach to the template
    29 ###    nocleanup:  (optional) Leave the remote script after execution? (default to false)
    29 ###    nocleanup:  (optional) Leave the remote script after execution? (default to false)
    30 ###    tempdir:    (optional) The destination temp directory.  (defaults to /tmp)
    30 ###    delete_cmd: (optional) The command to delete the remote script.  (default to 'rm')
       
    31 ###    run_binary: (optional) Windows doesn't allow direct execution of scripts, this is prefixed to the remote command if present.
       
    32 ###    tempdir:    (optional) The destination temp directory.  (defaults to /tmp/, needs to include the separator character)
    31 ###
    33 ###
    32 ###
    34 ###
    33 ### Additionally, this class responds to the 'symphony.ssh' configurability
    35 ### Additionally, this class responds to the 'symphony.ssh' configurability
    34 ### key.  Currently, you can override the default ssh user and private key.
    36 ### key.  Currently, you can override the default ssh user and private key.
    35 ###
    37 ###
    77 	### execute it.
    79 	### execute it.
    78 	###
    80 	###
    79 	def work( payload, metadata )
    81 	def work( payload, metadata )
    80 		template   = payload[ 'template' ]
    82 		template   = payload[ 'template' ]
    81 		attributes = payload[ 'attributes' ] || {}
    83 		attributes = payload[ 'attributes' ] || {}
    82 		port       = payload[ 'port' ]    || 22
       
    83 		user       = payload[ 'user' ]    || Symphony::Task::SSH.user
    84 		user       = payload[ 'user' ]    || Symphony::Task::SSH.user
    84 		key        = payload[ 'key'  ]    || Symphony::Task::SSH.key
    85 		key        = payload[ 'key'  ]    || Symphony::Task::SSH.key
    85 		nocleanup  = payload[ 'nocleanup' ]
    86 		tempdir    = payload[ 'tempdir' ] || '/tmp/'
    86 		tempdir    = payload[ 'tempdir' ] || '/tmp'
       
    87 
    87 
    88 		raise ArgumentError, "Missing required option 'template'" unless template
    88 		raise ArgumentError, "Missing required option 'template'" unless template
    89 		raise ArgumentError, "Missing required option 'host'"     unless payload[ 'host' ]
    89 		raise ArgumentError, "Missing required option 'host'"     unless payload[ 'host' ]
    90 
    90 
    91 		remote_filename = self.make_remote_filename( template, tempdir )
    91 		remote_filename = self.make_remote_filename( template, tempdir )
    92 		source = self.generate_script( template, attributes )
    92 		source = self.generate_script( template, attributes )
    93 
    93 
    94 		ssh_options = DEFAULT_SSH_OPTIONS.merge( port: port, keys: Array(key) )
    94 		# Map any configuration parameters in the payload to ssh
       
    95 		# options, for potential per-message behavior overrides.
       
    96 		ssh_opts_override = payload.
       
    97 			slice( *DEFAULT_SSH_OPTIONS.keys.map( &:to_s ) ).
       
    98 			transform_keys{|k| k.to_sym }
       
    99 
       
   100 		ssh_options = DEFAULT_SSH_OPTIONS.dup.merge!(
       
   101 			ssh_opts_override,
       
   102 			port: payload[ 'port' ] || 22,
       
   103 			keys: Array( key )
       
   104 		)
    95 		ssh_options.merge!(
   105 		ssh_options.merge!(
    96 			logger: Loggability[ Net::SSH ],
   106 			logger: Loggability[ Net::SSH ],
    97 			verbose: :debug
   107 			verbose: :debug
    98 		) if payload[ 'debug' ]
   108 		) if payload[ 'debug' ]
    99 
   109 
   101 			self.log.debug "Uploading script (%d bytes) to %s:%s." %
   111 			self.log.debug "Uploading script (%d bytes) to %s:%s." %
   102 				[ source.bytesize, payload['host'], remote_filename ]
   112 				[ source.bytesize, payload['host'], remote_filename ]
   103 			self.upload_script( conn, source, remote_filename )
   113 			self.upload_script( conn, source, remote_filename )
   104 			self.log.debug "  done with the upload."
   114 			self.log.debug "  done with the upload."
   105 
   115 
   106 			self.run_script( conn, remote_filename, nocleanup )
   116 			self.run_script( conn, remote_filename, payload )
   107 			self.log.debug "Output was:\n#{@output}"
   117 			self.log.debug "Output was:\n#{@output}"
   108 		end
   118 		end
   109 
   119 
   110 		return true
   120 		return true
   111 	end
   121 	end
   116 	#########
   126 	#########
   117 
   127 
   118 	### Generate a unique filename for the script on the remote host,
   128 	### Generate a unique filename for the script on the remote host,
   119 	### based on +template+ name.
   129 	### based on +template+ name.
   120 	###
   130 	###
   121 	def make_remote_filename( template, tempdir="/tmp" )
   131 	def make_remote_filename( template, tempdir="/tmp/" )
   122 		basename = File.basename( template, File.extname(template) )
   132 		basename = File.basename( template, File.extname(template) )
   123 		tmpname  = "%s/%s-%s" % [
   133 		tmpname  = "%s%s-%s" % [
   124 			tempdir,
   134 			tempdir,
   125 			basename,
   135 			basename,
   126 			SecureRandom.hex( 6 )
   136 			SecureRandom.hex( 6 )
   127 		]
   137 		]
   128 
   138 
   151 		end
   161 		end
   152 	end
   162 	end
   153 
   163 
   154 
   164 
   155 	### Run the +remote_filename+ via the ssh +conn+.  The script
   165 	### Run the +remote_filename+ via the ssh +conn+.  The script
   156 	### will be deleted automatically unless +nocleanup+ is true.
   166 	### will be deleted automatically unless +nocleanup+ is set
       
   167 	### in the payload.
   157 	###
   168 	###
   158 	def run_script( conn, remote_filename, nocleanup=false )
   169 	def run_script( conn, remote_filename, payload )
   159 		@output = conn.exec!( remote_filename )
   170 		delete_cmd = payload[ 'delete_cmd' ] || 'rm'
   160 		conn.exec!( "rm #{remote_filename}" ) unless nocleanup
   171 		command    = remote_filename
       
   172 		command    = "%s %s" % [ payload['run_binary'], remote_filename ] if payload[ 'run_binary' ]
       
   173 
       
   174 		@output = conn.exec!( command )
       
   175 		conn.exec!( "#{delete_cmd} #{remote_filename}" ) unless payload[ 'nocleanup' ]
   161 	end
   176 	end
   162 
   177 
   163 end # Symphony::Task::SSHScript
   178 end # Symphony::Task::SSHScript
   164 
   179