symphony 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,126 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'shellwords'
4
- require 'symphony/task' unless defined?( Symphony::Task )
5
-
6
-
7
- ### A base SSH class for connecting to remote hosts, running commands,
8
- ### and collecting output.
9
- class Symphony::Task::SSH < Symphony::Task
10
- extend MethodUtilities
11
-
12
- ### Create a new SSH task for the given +job+ and +queue+.
13
- def initialize( queue, job )
14
- super
15
-
16
- # The default path to the ssh binary.
17
- @path = self.options[:ssh_path] || '/usr/bin/ssh'
18
-
19
- # Default ssh behavior arguments.
20
- @ssh_args = self.options[:ssh_args] || [
21
- '-e', 'none',
22
- '-T',
23
- '-x',
24
- '-q',
25
- '-o', 'CheckHostIP=no',
26
- '-o', 'BatchMode=yes',
27
- '-o', 'StrictHostKeyChecking=no'
28
- ]
29
-
30
- # required arguments
31
- @hostname = self.options[:hostname] or raise ArgumentError, "no hostname specified"
32
- @command = self.options[:command] or raise ArgumentError, "no command specified"
33
-
34
- # optional arguments
35
- @port = self.options[:port] || 22
36
- @user = self.options[:user] || 'root'
37
- @key = self.options[:key]
38
-
39
- @output = nil
40
- @return_value = nil
41
- end
42
-
43
- # The default path to the ssh binary.
44
- attr_reader :path
45
-
46
- # Default ssh behavior arguments.
47
- attr_reader :ssh_args
48
-
49
- # The hostname to connect to.
50
- attr_reader :hostname
51
-
52
- # The command to run on the remote host.
53
- attr_reader :command
54
-
55
- # The key to use for authentication.
56
- attr_reader :key
57
-
58
- # The remote ssh port.
59
- attr_reader :port
60
-
61
- # Connect to the remote host as this user. Defaults to 'root'.
62
- attr_reader :user
63
-
64
-
65
- ### Call ssh and capture output.
66
- def run
67
- @return_value = self.open_connection do |reader, writer|
68
- self.log.debug "Writing command #{self.command}..."
69
- writer.puts( self.command )
70
- self.log.debug " closing child's writer."
71
- writer.close
72
- self.log.debug " reading from child."
73
- reader.read
74
- end
75
- end
76
-
77
-
78
- ### Emit the output from the remote ssh call
79
- def on_completion
80
- if @return_value
81
- self.log.info "Remote exited with %d, output: %s" % [ @return_value.exitstatus, @output ]
82
- end
83
- end
84
-
85
-
86
- #########
87
- protected
88
- #########
89
-
90
- ### Call ssh and yield the remote IO objects to the caller,
91
- ### cleaning up afterwards.
92
- def open_connection
93
- raise LocalJumpError, "no block given" unless block_given?
94
-
95
- fqdn = self.expand_hostname( self.hostname ).
96
- find {|hostname| self.ping(hostname, self.port) } or
97
- raise "Unable to find an on-network host for %s:%d" % [ self.hostname, self.port ]
98
-
99
- cmd = []
100
- cmd << self.path
101
- cmd += self.ssh_args
102
- cmd << '-p' << self.port.to_s
103
- cmd << '-i' << self.key if self.key
104
- cmd << '-l' << self.user
105
- cmd << fqdn
106
- cmd.flatten!
107
- self.log.debug "Running SSH command with: %p" % [ Shellwords.shelljoin(cmd) ]
108
-
109
- parent_reader, child_writer = IO.pipe
110
- child_reader, parent_writer = IO.pipe
111
-
112
- pid = spawn( *cmd, :out => child_writer, :in => child_reader, :close_others => true )
113
- child_writer.close
114
- child_reader.close
115
-
116
- self.log.debug "Yielding back to the run block."
117
- @output = yield( parent_reader, parent_writer )
118
- self.log.debug " run block done."
119
-
120
- pid, status = Process.waitpid2( pid )
121
- return status
122
- end
123
-
124
-
125
- end # class Symphony::Task::SSH
126
-
@@ -1,168 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'net/ssh'
4
- require 'net/sftp'
5
- require 'tmpdir'
6
- require 'inversion'
7
- require 'symphony/task' unless defined?( Symphony::Task )
8
-
9
-
10
- # A task to execute a script on a remote host via SSH.
11
- class Symphony::Task::SSHScript < Symphony::Task
12
- extend Loggability,
13
- MethodUtilities
14
-
15
- # Loggability API -- Log to symphony's logger
16
- log_to :symphony
17
-
18
-
19
- # Template config
20
- TEMPLATE_OPTS = {
21
- :ignore_unknown_tags => false,
22
- :on_render_error => :propagate,
23
- :strip_tag_lines => true
24
- }
25
-
26
- # The defaults to use when connecting via SSH
27
- DEFAULT_SSH_OPTIONS = {
28
- :auth_methods => [ "publickey" ],
29
- :compression => true,
30
- :config => false,
31
- :keys_only => true,
32
- # :logger => Loggability[ Net::SSH ],
33
- :paranoid => false,
34
- :timeout => 10.seconds,
35
- # :verbose => :debug,
36
- :global_known_hosts_file => '/dev/null',
37
- :user_known_hosts_file => '/dev/null',
38
- }
39
-
40
-
41
- ### Create a new SSH task for the given +job+ and +queue+.
42
- def initialize( queue, job )
43
- super
44
-
45
- # required arguments
46
- @hostname = self.options[:hostname] or raise ArgumentError, "no hostname specified"
47
- @template = self.options[:template] or raise ArgumentError, "no script template specified"
48
- @key = self.options[:key] or raise ArgumentError, "no private key specified"
49
-
50
- # optional arguments
51
- @port = self.options[:port] || 22
52
- @user = self.options[:user] || 'root'
53
- @attributes = self.options[:attributes] || {}
54
- @nocleanup = self.options[:nocleanup] ? true : false
55
- end
56
-
57
-
58
- ######
59
- public
60
- ######
61
-
62
- # The name of the host to connect to
63
- attr_reader :hostname
64
-
65
- # The path to the script template
66
- attr_accessor :template
67
-
68
- # The path to the SSH key to use for auth
69
- attr_reader :key
70
-
71
- # The SSH port to use
72
- attr_reader :port
73
-
74
- # The user to connect as
75
- attr_reader :user
76
-
77
- # Attributes that will be set on the script template.
78
- attr_reader :attributes
79
-
80
- # Flag that will cause the uploaded script to not be cleaned up after running. Useful for
81
- # diagnostics.
82
- attr_reader :nocleanup
83
-
84
-
85
- ### Load the script as an Inversion template, sending and executing
86
- ### it on the remote host.
87
- def run
88
- fqdn = self.expand_hostname( self.hostname ).
89
- find {|hostname| self.ping(hostname, self.port) }
90
-
91
- unless fqdn
92
- self.log.debug "Unable to find an on-network host for %s:%d" %
93
- [ self.hostname, self.port ]
94
- return
95
- end
96
-
97
- remote_filename = self.make_remote_filename
98
- source = self.generate_script
99
-
100
- # Establish the SSH connection
101
- ssh_options = DEFAULT_SSH_OPTIONS.merge( :port => self.port, :keys => [self.key] )
102
- self.with_timeout do
103
- Net::SSH.start( fqdn, self.user, ssh_options ) do |conn|
104
- self.upload_script( conn, source, remote_filename )
105
- self.run_script( conn, remote_filename )
106
- end
107
- end
108
- end
109
-
110
-
111
- #########
112
- protected
113
- #########
114
-
115
- ### Return a human-readable description of details of the task.
116
- def description
117
- return "Running script '%s' on '%s:%d' as '%s'" % [
118
- File.basename( self.template ),
119
- self.hostname,
120
- self.port,
121
- self.user,
122
- ]
123
- end
124
-
125
-
126
- ### Generate a unique filename for the script on the remote host.
127
- def make_remote_filename
128
- template = self.template
129
- basename = File.basename( template, File.extname(template) )
130
-
131
- tmpname = Dir::Tmpname.make_tmpname( basename, Process.pid )
132
-
133
- return "/tmp/#{tmpname}"
134
- end
135
-
136
-
137
- ### Generate a script by loading the script template, populating it with
138
- ### attributes, and rendering it.
139
- def generate_script
140
- tmpl = Inversion::Template.load( self.template, TEMPLATE_OPTS )
141
-
142
- tmpl.attributes.merge!( self.attributes )
143
- tmpl.task = self
144
-
145
- return tmpl.render
146
- end
147
-
148
- ### Render the given +template+ as script source, then use the specified +conn+ object
149
- ### to upload it.
150
- def upload_script( conn, source, remote_filename )
151
- self.log.debug "Uploading script (%d bytes) to %s:%s." %
152
- [ source.bytesize, self.hostname, remote_filename ]
153
- conn.sftp.file.open( remote_filename, "w", 0755 ) do |fh|
154
- fh.print( source )
155
- end
156
- self.log.debug " done with the upload."
157
- end
158
-
159
-
160
- ### Run the script on the remote host.
161
- def run_script( conn, remote_filename )
162
- output = conn.exec!( remote_filename )
163
- self.log.debug "Output was:\n#{output}"
164
- conn.exec!( "rm #{remote_filename}" ) unless self.nocleanup
165
- end
166
-
167
- end # class Symphony::Task::SSHScript
168
-