symphony 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
-