lvmsync 3.1.3 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/bin/lvmsync +67 -28
  2. metadata +4 -4
data/bin/lvmsync CHANGED
@@ -28,7 +28,8 @@ include LVM::Helpers
28
28
 
29
29
  def main()
30
30
  # Parse me some options
31
- options = {}
31
+ options = { :rsh => "ssh" }
32
+
32
33
  OptionParser.new do |opts|
33
34
  opts.banner = "Usage: lvmsync [options]"
34
35
  opts.separator ""
@@ -40,19 +41,35 @@ def main()
40
41
  opts.on("--server", "Run in server mode (deprecated; use '--apply -' instead)") do |v|
41
42
  options[:server] = true
42
43
  end
44
+
43
45
  opts.on("-v", "--[no-]verbose",
44
- "Run verbosely") { |v| options[:verbose] = true }
46
+ "Run verbosely") { |v| options[:verbose] = v }
47
+
48
+ opts.on("-q", "--[no-]quiet",
49
+ "Run quietly") { |v| options[:quiet] = v }
50
+
45
51
  opts.on("-b <file>", "--snapback <file>",
46
52
  "Make a backup snapshot file on the destination") do |v|
47
53
  options[:snapback] = v
48
54
  end
55
+
49
56
  opts.on("-a", "--apply <file>",
50
57
  "Apply mode: write the contents of a snapback file to a device") do |v|
51
58
  options[:apply] = v
52
59
  end
60
+
53
61
  opts.on("-s", "--stdout", "Write output data to stdout rather than another lvmsync process") do |v|
54
62
  options[:stdout] = true
55
63
  end
64
+
65
+ opts.on("-r", "--data-source", "Read data blocks from a block device other than the snapshot origin") do |v|
66
+ options[:source] = v
67
+ end
68
+
69
+ opts.on("-e", "--rsh <command-string>", "Use specified command when invoking SSH") do |v|
70
+ options[:rsh] = v
71
+ end
72
+
56
73
  opts.on("-V", "--version", "Print version of lvmsync") do |v|
57
74
  begin
58
75
  puts "lvmsync #{GVB.version}"
@@ -65,6 +82,11 @@ def main()
65
82
  end
66
83
  end.parse!
67
84
 
85
+ if options[:quiet] and options[:verbose]
86
+ $stderr.puts "I can't run quietly *and* verbosely at the same time!"
87
+ exit 1
88
+ end
89
+
68
90
  if options[:apply]
69
91
  if ARGV[0].nil?
70
92
  $stderr.puts "No destination device specified."
@@ -73,7 +95,7 @@ def main()
73
95
  options[:device] = ARGV[0]
74
96
  run_apply(options)
75
97
  elsif options[:server]
76
- $stderr.puts "--server is deprecated; please use '--apply -' instead"
98
+ $stderr.puts "--server is deprecated; please use '--apply -' instead" unless opts[:quiet]
77
99
  if (ARGV[0].nil?)
78
100
  $stderr.puts "No destination block device specified. WTF?"
79
101
  exit 1
@@ -176,8 +198,6 @@ def run_client(opts)
176
198
  exit 1
177
199
  end
178
200
 
179
- $stderr.puts "Origin device: #{lv.origin.path}" if opts[:verbose]
180
-
181
201
  # Since, in principle, we're not supposed to be reading from snapshot
182
202
  # devices directly, the kernel makes no attempt to make the device's read
183
203
  # cache stay in sync with the actual state of the device. As a result,
@@ -187,12 +207,15 @@ def run_client(opts)
187
207
 
188
208
  snapback = opts[:snapback] ? "--snapback #{opts[:snapback]}" : ''
189
209
 
210
+ source = opts[:source] || lv.origin.path
211
+ $stderr.puts "Data source: #{source}" if opts[:verbose]
212
+
190
213
  if opts[:stdout]
191
- dump_changes(lv, $stdout, opts)
214
+ dump_changes(lv, source, $stdout, opts)
192
215
  else
193
216
  verbose = opts[:verbose] ? '-v' : ''
194
217
  server_cmd = if desthost
195
- "ssh #{desthost} lvmsync --apply - #{snapback} #{verbose} #{destdev}"
218
+ "#{opts[:rsh]} #{desthost} lvmsync --apply - #{snapback} #{verbose} #{destdev}"
196
219
  else
197
220
  "#{$0} --apply - #{snapback} #{verbose} #{destdev}"
198
221
  end
@@ -201,24 +224,38 @@ def run_client(opts)
201
224
  errors = nil
202
225
 
203
226
  Open3.popen3(server_cmd) do |stdin_fd, stdout_fd, stderr_fd, wait_thr|
204
- dump_changes(lv, stdin_fd, opts) do
205
- more_to_read = true
206
- while more_to_read
207
- more_to_read = false
208
- (IO.select([stdout_fd, stderr_fd], [], [], 0) || [[]])[0].each do |fd|
209
- more_to_read = true
227
+ fds = [stdout_fd, stderr_fd]
228
+
229
+ dump_changes(lv, source, stdin_fd, opts) do
230
+ # Remember that this fires between *every* block sent to the
231
+ # receiver, so don't do anything particularly slow in here!
232
+ until (active_fds = IO.select(fds, [], [], 0)).nil?
233
+ active_fds[0].each do |fd|
210
234
  begin
211
- $stderr.puts "\e[2K\rremote:#{fd.readline}"
235
+ $stderr.puts "\e[2K\rremote:#{fd.readline}" unless opts[:quiet]
212
236
  rescue EOFError, Errno::EPIPE
213
- # lolwhateva
237
+ fd.close
238
+ fds.delete(fd)
214
239
  end
215
240
  end
216
241
  end
217
242
  end
243
+
218
244
  stdin_fd.close
219
- [stderr_fd, stdout_fd].each do |fd|
220
- until fd.eof?
221
- $stderr.puts "\e[2K\rremote:#{fd.readline}"
245
+
246
+ # Read any residual data that might be left in the stdout/stderr of
247
+ # the remote; we've got to do this with a timeout because of
248
+ # OpenSSH, which, when used in ControlMaster ("multiplexing") mode,
249
+ # holds open stderr, meaning that IO.select will never indicate
250
+ # that stderr is finished.
251
+ until (active_fds = IO.select(fds, [], [], 0.1)).nil?
252
+ active_fds[0].each do |fd|
253
+ begin
254
+ $stderr.puts "\e[2K\rremote:#{fd.readline}" unless opts[:quiet]
255
+ rescue EOFError, Errno::EPIPE
256
+ fd.close
257
+ fds.delete(fd)
258
+ end
222
259
  end
223
260
  end
224
261
  exit_status = wait_thr.value if wait_thr
@@ -230,23 +267,23 @@ def run_client(opts)
230
267
  end
231
268
  end
232
269
 
233
- def dump_changes(lv, outfd, opts)
270
+ def dump_changes(snapshot, source, outfd, opts)
234
271
  outfd.puts PROTOCOL_VERSION
235
272
 
236
273
  start_time = Time.now
237
274
  xfer_count = 0
238
275
  xfer_size = 0
239
276
  total_size = 0
240
- change_count = lv.changes.length
277
+ change_count = snapshot.changes.length
241
278
 
242
- File.open(lv.origin.path, 'r') do |origindev|
243
- lv.changes.each do |r|
279
+ File.open(source, 'r') do |origindev|
280
+ snapshot.changes.each do |r|
244
281
  xfer_count += 1
245
282
  chunk_size = r.last - r.first + 1
246
283
  xfer_size += chunk_size
247
284
 
248
285
  $stderr.puts "Sending chunk #{r.to_s}..." if opts[:verbose]
249
- $stderr.puts "Seeking to #{r.first} in #{lv.origin.path}" if opts[:verbose]
286
+ $stderr.puts "Seeking to #{r.first} in #{source}" if opts[:verbose]
250
287
 
251
288
  origindev.seek(r.first, IO::SEEK_SET)
252
289
 
@@ -260,7 +297,7 @@ def dump_changes(lv, outfd, opts)
260
297
  end
261
298
 
262
299
  # Progress bar!
263
- if xfer_count % 100 == 50
300
+ if xfer_count % 100 == 50 and !opts[:quiet]
264
301
  $stderr.printf "\e[2K\rSending chunk %i of %i, %.2fMB/s",
265
302
  xfer_count,
266
303
  change_count,
@@ -274,11 +311,13 @@ def dump_changes(lv, outfd, opts)
274
311
  total_size = origindev.tell
275
312
  end
276
313
 
277
- $stderr.printf "\rTransferred %i bytes in %.2f seconds\n",
278
- xfer_size, Time.now - start_time
314
+ unless opts[:quiet]
315
+ $stderr.printf "\rTransferred %i bytes in %.2f seconds\n",
316
+ xfer_size, Time.now - start_time
279
317
 
280
- $stderr.printf "You transferred your changes %.2fx faster than a full dd!\n",
281
- total_size.to_f / xfer_size
318
+ $stderr.printf "You transferred your changes %.2fx faster than a full dd!\n",
319
+ total_size.to_f / xfer_size
320
+ end
282
321
  end
283
322
 
284
323
  # Take a device name in any number of different formats and return a [VG, LV] pair.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lvmsync
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.3
4
+ version: 3.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-10-12 00:00:00.000000000 Z
12
+ date: 2014-10-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: git-version-bump
@@ -238,7 +238,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
238
238
  version: '0'
239
239
  segments:
240
240
  - 0
241
- hash: -1253006935794364430
241
+ hash: -3943591993715626870
242
242
  required_rubygems_version: !ruby/object:Gem::Requirement
243
243
  none: false
244
244
  requirements:
@@ -247,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
247
247
  version: '0'
248
248
  segments:
249
249
  - 0
250
- hash: -1253006935794364430
250
+ hash: -3943591993715626870
251
251
  requirements: []
252
252
  rubyforge_project:
253
253
  rubygems_version: 1.8.23