Revision f9323011

b/daemons/import-export
207 207

  
208 208

  
209 209
def ProcessChildIO(child, socat_stderr_read_fd, dd_stderr_read_fd,
210
                   dd_pid_read_fd, status_file, child_logger,
210
                   dd_pid_read_fd, exp_size_read_fd, status_file, child_logger,
211 211
                   signal_notify, signal_handler, mode):
212 212
  """Handles the child processes' output.
213 213

  
......
221 221
  socat_stderr_read = os.fdopen(socat_stderr_read_fd, "r", 0)
222 222
  dd_stderr_read = os.fdopen(dd_stderr_read_fd, "r", 0)
223 223
  dd_pid_read = os.fdopen(dd_pid_read_fd, "r", 0)
224
  exp_size_read = os.fdopen(exp_size_read_fd, "r", 0)
224 225

  
225 226
  tp_samples = DD_THROUGHPUT_SAMPLES
226 227

  
228
  if options.exp_size == constants.IE_CUSTOM_SIZE:
229
    exp_size = None
230
  else:
231
    exp_size = options.exp_size
232

  
227 233
  child_io_proc = impexpd.ChildIOProcessor(options.debug, status_file,
228
                                           child_logger,
229
                                           throughput_samples=tp_samples)
234
                                           child_logger, tp_samples,
235
                                           exp_size)
230 236
  try:
231 237
    fdmap = {
232 238
      child.stderr.fileno():
......
237 243
        (dd_pid_read, child_io_proc.GetLineSplitter(impexpd.PROG_DD_PID)),
238 244
      dd_stderr_read.fileno():
239 245
        (dd_stderr_read, child_io_proc.GetLineSplitter(impexpd.PROG_DD)),
246
      exp_size_read.fileno():
247
        (exp_size_read, child_io_proc.GetLineSplitter(impexpd.PROG_EXP_SIZE)),
240 248
      signal_notify.fileno(): (signal_notify, None),
241 249
      }
242 250

  
......
372 380
                    type="choice", help="Compression method",
373 381
                    metavar="[%s]" % "|".join(constants.IEC_ALL),
374 382
                    choices=list(constants.IEC_ALL), default=constants.IEC_GZIP)
383
  parser.add_option("--expected-size", dest="exp_size", action="store",
384
                    type="string", default=None,
385
                    help="Expected import/export size (MiB)")
375 386
  parser.add_option("--cmd-prefix", dest="cmd_prefix", action="store",
376 387
                    type="string", help="Command prefix")
377 388
  parser.add_option("--cmd-suffix", dest="cmd_suffix", action="store",
......
390 401
    # Won't return
391 402
    parser.error("Invalid mode: %s" % mode)
392 403

  
404
  if (options.exp_size is not None and
405
      options.exp_size != constants.IE_CUSTOM_SIZE):
406
    try:
407
      options.exp_size = int(options.exp_size)
408
    except (ValueError, TypeError), err:
409
      # Won't return
410
      parser.error("Invalid value for --expected-size: %s (%s)" %
411
                   (options.exp_size, err))
412

  
393 413
  return (status_file_path, mode)
394 414

  
395 415

  
396 416
class ChildProcess(subprocess.Popen):
397
  def __init__(self, cmd, noclose_fds):
417
  def __init__(self, env, cmd, noclose_fds):
398 418
    """Initializes this class.
399 419

  
400 420
    """
......
402 422

  
403 423
    # Not using close_fds because doing so would also close the socat stderr
404 424
    # pipe, which we still need.
405
    subprocess.Popen.__init__(self, cmd, shell=False, close_fds=False,
425
    subprocess.Popen.__init__(self, cmd, env=env, shell=False, close_fds=False,
406 426
                              stderr=subprocess.PIPE, stdout=None, stdin=None,
407 427
                              preexec_fn=self._ChildPreexec)
408 428
    self._SetProcessGroup()
......
474 494
      # Pipe to receive dd's PID
475 495
      (dd_pid_read_fd, dd_pid_write_fd) = os.pipe()
476 496

  
497
      # Pipe to receive size predicted by export script
498
      (exp_size_read_fd, exp_size_write_fd) = os.pipe()
499

  
477 500
      # Get child process command
478 501
      cmd_builder = impexpd.CommandBuilder(mode, options, socat_stderr_write_fd,
479 502
                                           dd_stderr_write_fd, dd_pid_write_fd)
480 503
      cmd = cmd_builder.GetCommand()
481 504

  
505
      # Prepare command environment
506
      cmd_env = os.environ.copy()
507

  
508
      if options.exp_size == constants.IE_CUSTOM_SIZE:
509
        cmd_env["EXP_SIZE_FD"] = str(exp_size_write_fd)
510

  
482 511
      logging.debug("Starting command %r", cmd)
483 512

  
484 513
      # Start child process
485
      child = ChildProcess(cmd, [socat_stderr_write_fd, dd_stderr_write_fd,
486
                                 dd_pid_write_fd])
514
      child = ChildProcess(cmd_env, cmd,
515
                           [socat_stderr_write_fd, dd_stderr_write_fd,
516
                            dd_pid_write_fd, exp_size_write_fd])
487 517
      try:
488 518
        def _ForwardSignal(signum, _):
489 519
          """Forwards signals to child process.
......
506 536
            utils.RetryOnSignal(os.close, socat_stderr_write_fd)
507 537
            utils.RetryOnSignal(os.close, dd_stderr_write_fd)
508 538
            utils.RetryOnSignal(os.close, dd_pid_write_fd)
539
            utils.RetryOnSignal(os.close, exp_size_write_fd)
509 540

  
510 541
            if ProcessChildIO(child, socat_stderr_read_fd, dd_stderr_read_fd,
511
                              dd_pid_read_fd, status_file, child_logger,
542
                              dd_pid_read_fd, exp_size_read_fd,
543
                              status_file, child_logger,
512 544
                              signal_wakeup, signal_handler, mode):
513 545
              # The child closed all its file descriptors and there was no
514 546
              # signal
b/lib/constants.py
223 223
  IEC_GZIP,
224 224
  ])
225 225

  
226
IE_CUSTOM_SIZE = "fd"
227

  
226 228
# Import/export I/O
227 229
# Direct file I/O, equivalent to a shell's I/O redirection using '<' or '>'
228 230
IEIO_FILE = "file"
b/lib/impexpd/__init__.py
82 82
(PROG_OTHER,
83 83
 PROG_SOCAT,
84 84
 PROG_DD,
85
 PROG_DD_PID) = range(1, 5)
85
 PROG_DD_PID,
86
 PROG_EXP_SIZE) = range(1, 6)
86 87
PROG_ALL = frozenset([
87 88
  PROG_OTHER,
88 89
  PROG_SOCAT,
89 90
  PROG_DD,
90 91
  PROG_DD_PID,
92
  PROG_EXP_SIZE,
91 93
  ])
92 94

  
93 95

  
......
274 276

  
275 277

  
276 278
class ChildIOProcessor(object):
277
  def __init__(self, debug, status_file, logger, throughput_samples):
279
  def __init__(self, debug, status_file, logger, throughput_samples, exp_size):
278 280
    """Initializes this class.
279 281

  
280 282
    """
......
291 293
    self._dd_progress = []
292 294

  
293 295
    # Expected size of transferred data
294
    self._exp_size = None
296
    self._exp_size = exp_size
295 297

  
296 298
  def GetLineSplitter(self, prog):
297 299
    """Returns the line splitter for a program.
......
389 391
      self._dd_pid = int(line)
390 392
      forward_line = None
391 393

  
394
    elif prog == PROG_EXP_SIZE:
395
      logging.debug("Received predicted size %r", line)
396
      forward_line = None
397

  
398
      if line:
399
        try:
400
          exp_size = utils.BytesToMebibyte(int(line))
401
        except (ValueError, TypeError), err:
402
          logging.error("Failed to convert predicted size %r to number: %s",
403
                        line, err)
404
          exp_size = None
405
      else:
406
        exp_size = None
407

  
408
      self._exp_size = exp_size
409

  
392 410
    if forward_line:
393 411
      self._logger.info(forward_line)
394 412
      self._status_file.AddRecentOutput(forward_line)
b/man/ganeti-os-interface.sgml
259 259
        DISK_... variables).
260 260
      </para>
261 261

  
262
      <para>
263
        To provide the user with an estimate on how long the export will take,
264
        a predicted size can be written to the file descriptor passed in the
265
        variable <envar>EXP_SIZE_FD</envar>. The value is in bytes and must be
266
        terminated by a newline character (\n). Older versions of Ganeti don't
267
        support this feature, hence the variable should be checked before use.
268
        Example: <screen>
269
if test -n "$EXP_SIZE_FD"; then
270
  blockdev --getsize64 $blockdev >&amp;$EXP_SIZE_FD
271
fi
272
</screen>
273
      </para>
274

  
262 275
    </refsect2>
263 276

  
264 277
    <refsect2>

Also available in: Unified diff