opennebula 4.90.10.rc1 → 5.0.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.
- checksums.yaml +4 -4
- data/lib/ActionManager.rb +278 -0
- data/lib/CommandManager.rb +242 -0
- data/lib/DriverExecHelper.rb +215 -0
- data/lib/OpenNebulaDriver.rb +233 -0
- data/lib/VirtualMachineDriver.rb +376 -0
- data/lib/cloud/CloudClient.rb +1 -1
- data/lib/opennebula/virtual_machine.rb +1 -0
- data/lib/opennebula.rb +1 -1
- data/lib/vcenter_driver.rb +2716 -0
- metadata +10 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 16cda589536bb5239ef70cb6898e8b00e3ebd9c9
|
|
4
|
+
data.tar.gz: 4e71745bc284fb49c762321fc7523064c3f11716
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cd3b03e5187e2efe9d6cee742e58e917b6fec606388282657c9a4a0b276facc22e859084b9e26d9447e2b664997727653ff33e6792d0744b80a7e7c16436da87
|
|
7
|
+
data.tar.gz: 3dec23410ed0b0b8f53f9bdec6bea441b70d1df9f8cdb3f1a270a8cbba313750565e16a0f2c78985c9f00184120c03252b53fc7e63ca1a111a4350116c70e239
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------- */
|
|
2
|
+
# Copyright 2002-2016, OpenNebula Project, OpenNebula Systems #
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may */
|
|
4
|
+
# not use this file except in compliance with the License. You may obtain */
|
|
5
|
+
# a copy of the License at */
|
|
6
|
+
# */
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0 */
|
|
8
|
+
# */
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software */
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS, */
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
|
|
12
|
+
# See the License for the specific language governing permissions and */
|
|
13
|
+
# limitations under the License. */
|
|
14
|
+
# -------------------------------------------------------------------------- */
|
|
15
|
+
|
|
16
|
+
require 'thread'
|
|
17
|
+
|
|
18
|
+
=begin rdoc
|
|
19
|
+
|
|
20
|
+
This class provides support to handle actions. Class methods, or actions, can be
|
|
21
|
+
registered in the action manager. The manager will wait for actions to be
|
|
22
|
+
triggered (thread-safe), and will execute them concurrently. The action manager
|
|
23
|
+
can be used to synchronize different objects in different threads
|
|
24
|
+
|
|
25
|
+
== Example
|
|
26
|
+
|
|
27
|
+
class Sample
|
|
28
|
+
attr_reader :am
|
|
29
|
+
|
|
30
|
+
def initialize
|
|
31
|
+
@am = ActionManager.new(15,true)
|
|
32
|
+
|
|
33
|
+
@am.register_action("SLEEP",method("sleep_action"))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def sleep_action(secs)
|
|
37
|
+
sleep(secs)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def finalize_action
|
|
41
|
+
p "Exiting..."
|
|
42
|
+
@am.stop_listener
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
s = Sample.new
|
|
48
|
+
|
|
49
|
+
s.@am.start_listener
|
|
50
|
+
|
|
51
|
+
# Objects in other threads can trigger actions like this
|
|
52
|
+
# s.am.trigger_action("SLEEP",rand(3)+1)
|
|
53
|
+
# s.am.trigger_action("FINALIZE")
|
|
54
|
+
=end
|
|
55
|
+
|
|
56
|
+
class ActionManager
|
|
57
|
+
|
|
58
|
+
# Creates a new Action Manager
|
|
59
|
+
#
|
|
60
|
+
# +concurrency+ is the maximun number of actions that can run at the same
|
|
61
|
+
# time
|
|
62
|
+
# +threaded+ if true actions will be executed by default in a different
|
|
63
|
+
# thread
|
|
64
|
+
def initialize(concurrency=10, threaded=true)
|
|
65
|
+
@finalize = false
|
|
66
|
+
@actions = Hash.new
|
|
67
|
+
@threaded = threaded
|
|
68
|
+
|
|
69
|
+
@concurrency = concurrency
|
|
70
|
+
@num_running = 0
|
|
71
|
+
|
|
72
|
+
@action_queue = Array.new
|
|
73
|
+
@action_running = Hash.new
|
|
74
|
+
|
|
75
|
+
@threads_mutex = Mutex.new
|
|
76
|
+
@threads_cond = ConditionVariable.new
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Registers a new action in the manager. An action is defined by:
|
|
80
|
+
#
|
|
81
|
+
# +aname+ name of the action, it will identify the action
|
|
82
|
+
# +method+ it's invoked with call. It should be a Proc or Method object
|
|
83
|
+
# +threaded+ execute the action in a new thread
|
|
84
|
+
def register_action(aname, method, threaded=nil)
|
|
85
|
+
threaded ||= @threaded
|
|
86
|
+
|
|
87
|
+
@actions[aname]={
|
|
88
|
+
:method => method,
|
|
89
|
+
:threaded => threaded
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Triggers the execution of the action.
|
|
94
|
+
#
|
|
95
|
+
# +aname+ name of the action
|
|
96
|
+
# +action_id+ an id to identify the action (to cancel it later)
|
|
97
|
+
# +aargs+ arguments to call the action
|
|
98
|
+
def trigger_action(aname, action_id, *aargs)
|
|
99
|
+
|
|
100
|
+
@threads_mutex.synchronize {
|
|
101
|
+
return if @finalize
|
|
102
|
+
|
|
103
|
+
if aname == :FINALIZE
|
|
104
|
+
finalize if respond_to?(:finalize)
|
|
105
|
+
@finalize = true
|
|
106
|
+
@threads_cond.signal if @num_running == 0
|
|
107
|
+
return
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
if !@actions.has_key?(aname)
|
|
111
|
+
return
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
arity=@actions[aname][:method].arity
|
|
115
|
+
|
|
116
|
+
if arity < 0
|
|
117
|
+
# Last parameter is an array
|
|
118
|
+
arity = -arity - 1
|
|
119
|
+
if arity > aargs.length
|
|
120
|
+
# Message has not enough parameters
|
|
121
|
+
return
|
|
122
|
+
end
|
|
123
|
+
# Converts last parameters to an array
|
|
124
|
+
aargs[arity..-1]=[aargs[arity..-1]]
|
|
125
|
+
else
|
|
126
|
+
if arity != aargs.length
|
|
127
|
+
return
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
@action_queue << @actions[aname].merge(:args => aargs,
|
|
132
|
+
:id => action_id)
|
|
133
|
+
|
|
134
|
+
if @num_running < @concurrency
|
|
135
|
+
@threads_cond.signal
|
|
136
|
+
end
|
|
137
|
+
}
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def cancel_action(action_id)
|
|
141
|
+
@threads_mutex.synchronize {
|
|
142
|
+
action = @action_running[action_id]
|
|
143
|
+
if action
|
|
144
|
+
thread = action[:thread]
|
|
145
|
+
else
|
|
146
|
+
thread = nil
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
if thread
|
|
150
|
+
thread.kill
|
|
151
|
+
|
|
152
|
+
@num_running -= 1
|
|
153
|
+
delete_running_action(action_id)
|
|
154
|
+
|
|
155
|
+
@threads_cond.signal
|
|
156
|
+
else
|
|
157
|
+
i = @action_queue.select{|x| x[:id] == action_id}.first
|
|
158
|
+
@action_queue.delete(i) if i
|
|
159
|
+
end
|
|
160
|
+
}
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def start_listener
|
|
164
|
+
while true
|
|
165
|
+
@threads_mutex.synchronize {
|
|
166
|
+
while ((@concurrency - @num_running)==0) || empty_queue
|
|
167
|
+
@threads_cond.wait(@threads_mutex)
|
|
168
|
+
|
|
169
|
+
return if (@finalize && @num_running == 0)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
run_action
|
|
173
|
+
}
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
protected
|
|
178
|
+
|
|
179
|
+
def delete_running_action(action_id)
|
|
180
|
+
@action_running.delete(action_id)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def get_runable_action
|
|
184
|
+
@action_queue.shift
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def empty_queue
|
|
188
|
+
@action_queue.size==0
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def run_action
|
|
192
|
+
action = get_runable_action
|
|
193
|
+
|
|
194
|
+
if action
|
|
195
|
+
@num_running += 1
|
|
196
|
+
|
|
197
|
+
if action[:threaded]
|
|
198
|
+
thread = Thread.new {
|
|
199
|
+
action[:method].call(*action[:args])
|
|
200
|
+
|
|
201
|
+
@threads_mutex.synchronize {
|
|
202
|
+
@num_running -= 1
|
|
203
|
+
delete_running_action(action[:id])
|
|
204
|
+
|
|
205
|
+
@threads_cond.signal
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
action[:thread] = thread
|
|
210
|
+
@action_running[action[:id]] = action
|
|
211
|
+
else
|
|
212
|
+
action[:method].call(*action[:args])
|
|
213
|
+
|
|
214
|
+
@num_running -= 1
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
if __FILE__ == $0
|
|
221
|
+
|
|
222
|
+
class Sample
|
|
223
|
+
attr_reader :am
|
|
224
|
+
|
|
225
|
+
def initialize
|
|
226
|
+
@am = ActionManager.new(15,true)
|
|
227
|
+
|
|
228
|
+
@am.register_action(:SLEEP,method("sleep_action"))
|
|
229
|
+
# @am.register_action(:SLEEP,Proc.new{|s,i| p s ; sleep(s)})
|
|
230
|
+
@am.register_action(:NOP,method("nop_action"))
|
|
231
|
+
|
|
232
|
+
def @am.get_runable_action
|
|
233
|
+
action = super
|
|
234
|
+
puts "getting: #{action.inspect}"
|
|
235
|
+
action
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def @am.delete_running_action(action_id)
|
|
239
|
+
puts "deleting: #{action_id}"
|
|
240
|
+
super(action_id)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def sleep_action(secs, id)
|
|
245
|
+
p "ID: #{id} sleeping #{secs} seconds"
|
|
246
|
+
sleep(secs)
|
|
247
|
+
p "ID: #{id} Awaken!"
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def nop_action
|
|
251
|
+
p " - Just an action"
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
s = Sample.new
|
|
257
|
+
|
|
258
|
+
Thread.new {
|
|
259
|
+
sleep 1
|
|
260
|
+
|
|
261
|
+
100.times {|n|
|
|
262
|
+
s.am.trigger_action(:SLEEP,n,rand(3)+1,n)
|
|
263
|
+
s.am.trigger_action(:NOP,100+n)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
s.am.trigger_action(:SLEEP,301,5,301)
|
|
267
|
+
|
|
268
|
+
s.am.cancel_action(301)
|
|
269
|
+
|
|
270
|
+
s.am.trigger_action(:FINALIZE,0)
|
|
271
|
+
|
|
272
|
+
s.am.trigger_action(:SLEEP,999,rand(3)+1,999)
|
|
273
|
+
s.am.trigger_action(:SLEEP,333,rand(3)+1,333)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
s.am.start_listener
|
|
277
|
+
end
|
|
278
|
+
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Copyright 2002-2016, OpenNebula Project, OpenNebula Systems
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
5
|
+
# not use this file except in compliance with the License. You may obtain
|
|
6
|
+
# a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
# --------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
require 'pp'
|
|
18
|
+
require 'open3'
|
|
19
|
+
require 'stringio'
|
|
20
|
+
|
|
21
|
+
# Generic command executor that holds the code shared by all the command
|
|
22
|
+
# executors.
|
|
23
|
+
#
|
|
24
|
+
# Properties:
|
|
25
|
+
#
|
|
26
|
+
# * +code+: integer holding the exit code. Read-only
|
|
27
|
+
# * +stdout+: string of the standard output. Read-only
|
|
28
|
+
# * +stderr+: string of the standard error. Read-only
|
|
29
|
+
# * +command+: command to execute. Read-only
|
|
30
|
+
#
|
|
31
|
+
# The protocol for scripts to log is as follows:
|
|
32
|
+
#
|
|
33
|
+
# * Log messages will be sent to STDOUT
|
|
34
|
+
# * The script will return 0 if it succeded or any other value
|
|
35
|
+
# if there was a failure
|
|
36
|
+
# * In case of failure the cause of the error will be written to STDERR
|
|
37
|
+
# wrapped by start and end marks as follows:
|
|
38
|
+
#
|
|
39
|
+
# ERROR MESSAGE --8<------
|
|
40
|
+
# error message for the failure
|
|
41
|
+
# ERROR MESSAGE ------>8--
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class GenericCommand
|
|
45
|
+
attr_reader :code, :stdout, :stderr, :command
|
|
46
|
+
|
|
47
|
+
# Creates a command and runs it
|
|
48
|
+
def self.run(command, logger=nil, stdin=nil)
|
|
49
|
+
cmd = self.new(command, logger, stdin)
|
|
50
|
+
cmd.run
|
|
51
|
+
cmd
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Creates the new command:
|
|
55
|
+
# +command+: string with the command to be executed
|
|
56
|
+
# +logger+: proc that takes a message parameter and logs it
|
|
57
|
+
def initialize(command, logger=nil, stdin=nil)
|
|
58
|
+
@command = command
|
|
59
|
+
@logger = logger
|
|
60
|
+
@stdin = stdin
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Sends a log message to the logger proc
|
|
64
|
+
def log(message)
|
|
65
|
+
@logger.call(message) if @logger
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Runs the command
|
|
69
|
+
def run
|
|
70
|
+
std = execute
|
|
71
|
+
|
|
72
|
+
# Close standard IO descriptors
|
|
73
|
+
if @stdin
|
|
74
|
+
std[0] << @stdin
|
|
75
|
+
std[0].flush
|
|
76
|
+
end
|
|
77
|
+
std[0].close if !std[0].closed?
|
|
78
|
+
|
|
79
|
+
@stdout=std[1].read
|
|
80
|
+
std[1].close if !std[1].closed?
|
|
81
|
+
|
|
82
|
+
@stderr=std[2].read
|
|
83
|
+
std[2].close if !std[2].closed?
|
|
84
|
+
|
|
85
|
+
@code=get_exit_code(@stderr)
|
|
86
|
+
|
|
87
|
+
if @code!=0
|
|
88
|
+
log("Command execution fail: #{command}")
|
|
89
|
+
log(@stderr)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
return @code
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Parses error message from +stderr+ output
|
|
96
|
+
def get_error_message
|
|
97
|
+
tmp=@stderr.scan(/^ERROR MESSAGE --8<------\n(.*?)ERROR MESSAGE ------>8--$/m)
|
|
98
|
+
return "-" if !tmp[0]
|
|
99
|
+
tmp[0].join(' ').strip
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
# Gets exit code from STDERR
|
|
105
|
+
def get_exit_code(str)
|
|
106
|
+
tmp=str.scan(/^ExitCode: (\d*)$/)
|
|
107
|
+
return nil if !tmp[0]
|
|
108
|
+
tmp[0][0].to_i
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Low level command execution. This method has to be redefined
|
|
112
|
+
# for each kind of command execution. Returns an array with
|
|
113
|
+
# +stdin+, +stdout+ and +stderr+ handlers of the command execution.
|
|
114
|
+
def execute
|
|
115
|
+
puts "About to execute \"#{@command}\""
|
|
116
|
+
[StringIO.new, StringIO.new, StringIO.new]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Executes commands in the machine where it is called. See documentation
|
|
122
|
+
# for GenericCommand
|
|
123
|
+
class LocalCommand < GenericCommand
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
def execute
|
|
127
|
+
Open3.popen3("#{command} ; echo ExitCode: $? 1>&2")
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Executes commands in a remote machine ussing ssh. See documentation
|
|
132
|
+
# for GenericCommand
|
|
133
|
+
class SSHCommand < GenericCommand
|
|
134
|
+
attr_accessor :host
|
|
135
|
+
|
|
136
|
+
# Creates a command and runs it
|
|
137
|
+
def self.run(command, host, logger=nil, stdin=nil)
|
|
138
|
+
cmd=self.new(command, host, logger, stdin)
|
|
139
|
+
cmd.run
|
|
140
|
+
cmd
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# This one takes another parameter. +host+ is the machine
|
|
144
|
+
# where the command is going to be executed
|
|
145
|
+
def initialize(command, host, logger=nil, stdin=nil)
|
|
146
|
+
@host=host
|
|
147
|
+
super(command, logger, stdin)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
private
|
|
151
|
+
|
|
152
|
+
def execute
|
|
153
|
+
if @stdin
|
|
154
|
+
Open3.popen3("ssh #{@host} #{@command} ; echo ExitCode: $? 1>&2")
|
|
155
|
+
else
|
|
156
|
+
Open3.popen3("ssh -n #{@host} #{@command} ; echo ExitCode: $? 1>&2")
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
class RemotesCommand < SSHCommand
|
|
162
|
+
|
|
163
|
+
# Creates a command and runs it
|
|
164
|
+
def self.run(command, host, remote_dir, logger=nil, stdin=nil, retries=0)
|
|
165
|
+
cmd_file = command.split(' ')[0]
|
|
166
|
+
|
|
167
|
+
cmd_string = "'if [ -x \"#{cmd_file}\" ]; then #{command}; else\
|
|
168
|
+
exit #{MAGIC_RC}; fi'"
|
|
169
|
+
|
|
170
|
+
cmd = self.new(cmd_string, host, logger, stdin)
|
|
171
|
+
cmd.run
|
|
172
|
+
|
|
173
|
+
while cmd.code != 0 and retries != 0
|
|
174
|
+
if cmd.code == MAGIC_RC
|
|
175
|
+
update_remotes(host, remote_dir, logger)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
sleep 1
|
|
179
|
+
cmd.run
|
|
180
|
+
retries = retries - 1
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
cmd
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
private
|
|
187
|
+
|
|
188
|
+
ONE_LOCATION=ENV["ONE_LOCATION"]
|
|
189
|
+
|
|
190
|
+
if !ONE_LOCATION
|
|
191
|
+
REMOTES_LOCATION="/var/lib/one/remotes"
|
|
192
|
+
else
|
|
193
|
+
REMOTES_LOCATION=ONE_LOCATION+"/var/remotes/"
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
MAGIC_RC = 42
|
|
197
|
+
|
|
198
|
+
def self.update_remotes(host, remote_dir, logger=nil)
|
|
199
|
+
if logger != nil
|
|
200
|
+
logger.call("Remote worker node files not found")
|
|
201
|
+
logger.call("Updating remotes")
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
#recreate remote dir structure
|
|
205
|
+
SSHCommand.run("mkdir -p #{remote_dir}",host,logger)
|
|
206
|
+
|
|
207
|
+
# Use SCP to sync:
|
|
208
|
+
sync_cmd = "scp -rp #{REMOTES_LOCATION}/. #{host}:#{remote_dir}"
|
|
209
|
+
|
|
210
|
+
# Use rsync to sync:
|
|
211
|
+
# sync_cmd = "rsync -Laz #{REMOTES_LOCATION} #{host}:#{@remote_dir}"
|
|
212
|
+
LocalCommand.run(sync_cmd, logger)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
if $0 == __FILE__
|
|
218
|
+
|
|
219
|
+
command=GenericCommand.run("uname -a")
|
|
220
|
+
puts command.stderr
|
|
221
|
+
|
|
222
|
+
local_command=LocalCommand.run("uname -a")
|
|
223
|
+
puts "STDOUT:"
|
|
224
|
+
puts local_command.stdout
|
|
225
|
+
puts
|
|
226
|
+
puts "STDERR:"
|
|
227
|
+
puts local_command.stderr
|
|
228
|
+
|
|
229
|
+
ssh_command=SSHCommand.run("uname -a", "localhost")
|
|
230
|
+
puts "STDOUT:"
|
|
231
|
+
puts ssh_command.stdout
|
|
232
|
+
puts
|
|
233
|
+
puts "STDERR:"
|
|
234
|
+
puts ssh_command.stderr
|
|
235
|
+
|
|
236
|
+
fd = File.new("/etc/passwd")
|
|
237
|
+
str = String.new
|
|
238
|
+
fd.each {|line| str << line}
|
|
239
|
+
fd.close
|
|
240
|
+
|
|
241
|
+
ssh_in = SSHCommand.run("cat > /tmp/test","localhost",nil,str)
|
|
242
|
+
end
|