Revision 874f6148

b/lib/backend.py
1394 1394
        return
1395 1395

  
1396 1396
      try:
1397
        hyper.StopInstance(instance, retry=self.tried_once)
1397
        hyper.StopInstance(instance, retry=self.tried_once, timeout=timeout)
1398 1398
        if store_reason:
1399 1399
          _StoreInstReasonTrail(instance.name, reason)
1400 1400
      except errors.HypervisorError, err:
b/lib/hypervisor/hv_base.py
173 173
    """Start an instance."""
174 174
    raise NotImplementedError
175 175

  
176
  def StopInstance(self, instance, force=False, retry=False, name=None):
176
  def StopInstance(self, instance, force=False, retry=False, name=None,
177
                   timeout=None):
177 178
    """Stop an instance
178 179

  
179 180
    @type instance: L{objects.Instance}
......
186 187
    @param name: if this parameter is passed, the the instance object
187 188
        should not be used (will be passed as None), and the shutdown
188 189
        must be done by name only
190
    @type timeout: int or None
191
    @param timeout: if the parameter is not None, a soft shutdown operation will
192
        be killed after the specified number of seconds. A hard (forced)
193
        shutdown cannot have a timeout
189 194

  
190 195
    """
191 196
    raise NotImplementedError
b/lib/hypervisor/hv_chroot.py
167 167
      raise HypervisorError("Can't run the chroot start script: %s" %
168 168
                            result.output)
169 169

  
170
  def StopInstance(self, instance, force=False, retry=False, name=None):
170
  def StopInstance(self, instance, force=False, retry=False, name=None,
171
                   timeout=None):
171 172
    """Stop an instance.
172 173

  
173 174
    This method has complicated cleanup tests, as we must:
......
176 177
      - finally unmount the instance dir
177 178

  
178 179
    """
180
    assert(timeout is None or force is not None)
181

  
179 182
    if name is None:
180 183
      name = instance.name
181 184

  
......
183 186
    if not os.path.exists(root_dir) or not self._IsDirLive(root_dir):
184 187
      return
185 188

  
189
    timeout_cmd = []
190
    if timeout is not None:
191
      timeout_cmd.extend(["timeout", str(timeout)])
192

  
186 193
    # Run the chroot stop script only once
187 194
    if not retry and not force:
188
      result = utils.RunCmd(["chroot", root_dir, "/ganeti-chroot", "stop"])
195
      result = utils.RunCmd(timeout_cmd.extend(["chroot", root_dir,
196
                                                "/ganeti-chroot", "stop"]))
189 197
      if result.failed:
190 198
        raise HypervisorError("Can't run the chroot stop script: %s" %
191 199
                              result.output)
b/lib/hypervisor/hv_fake.py
165 165
      raise errors.HypervisorError("Failed to start instance %s: %s" %
166 166
                                   (instance.name, err))
167 167

  
168
  def StopInstance(self, instance, force=False, retry=False, name=None):
168
  def StopInstance(self, instance, force=False, retry=False, name=None,
169
                   timeout=None):
169 170
    """Stop an instance.
170 171

  
171 172
    For the fake hypervisor, this just removes the file in the base
172 173
    dir, if it exist, otherwise we raise an exception.
173 174

  
174 175
    """
176
    assert(timeout is None or force is not None)
177

  
175 178
    if name is None:
176 179
      name = instance.name
177 180
    if not self._IsAlive(name):
b/lib/hypervisor/hv_kvm.py
1696 1696
    # 500ms and likely more: socat can't detect the end of the reply and waits
1697 1697
    # for 500ms of no data received before exiting (500 ms is the default for
1698 1698
    # the "-t" parameter).
1699
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
1699
    socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
1700 1700
             (utils.ShellQuote(command),
1701
              timeout_cmd,
1701 1702
              constants.SOCAT_PATH,
1702 1703
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
1704

  
1703 1705
    result = utils.RunCmd(socat)
1704 1706
    if result.failed:
1705 1707
      msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
......
1776 1778
    else:
1777 1779
      return "pc"
1778 1780

  
1779
  def StopInstance(self, instance, force=False, retry=False, name=None):
1781
  def StopInstance(self, instance, force=False, retry=False, name=None,
1782
                   timeout=None):
1780 1783
    """Stop an instance.
1781 1784

  
1782 1785
    """
1786
    assert(timeout is None or force is not None)
1787

  
1783 1788
    if name is not None and not force:
1784 1789
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1785 1790
    if name is None:
......
1792 1797
      if force or not acpi:
1793 1798
        utils.KillProcess(pid)
1794 1799
      else:
1795
        self._CallMonitorCommand(name, "system_powerdown")
1800
        self._CallMonitorCommand(name, "system_powerdown", timeout)
1796 1801

  
1797 1802
  def CleanupInstance(self, instance_name):
1798 1803
    """Cleanup after a stopped instance
b/lib/hypervisor/hv_lxc.py
325 325
      raise HypervisorError("Running the lxc-start script failed: %s" %
326 326
                            result.output)
327 327

  
328
  def StopInstance(self, instance, force=False, retry=False, name=None):
328
  def StopInstance(self, instance, force=False, retry=False, name=None,
329
                   timeout=None):
329 330
    """Stop an instance.
330 331

  
331 332
    This method has complicated cleanup tests, as we must:
......
334 335
      - finally unmount the instance dir
335 336

  
336 337
    """
338
    assert(timeout is None or force is not None)
339

  
337 340
    if name is None:
338 341
      name = instance.name
339 342

  
343
    timeout_cmd = []
344
    if timeout is not None:
345
      timeout_cmd.extend(["timeout", str(timeout)])
346

  
340 347
    root_dir = self._InstanceDir(name)
341 348
    if not os.path.exists(root_dir):
342 349
      return
......
349 356
          raise HypervisorError("Running 'poweroff' on the instance"
350 357
                                " failed: %s" % result.output)
351 358
      time.sleep(2)
352
      result = utils.RunCmd(["lxc-stop", "-n", name])
359
      result = utils.RunCmd(timeout_cmd.extend(["lxc-stop", "-n", name]))
353 360
      if result.failed:
354 361
        logging.warning("Error while doing lxc-stop for %s: %s", name,
355 362
                        result.output)
......
358 365
      return
359 366

  
360 367
    for mpath in self._GetMountSubdirs(root_dir):
361
      result = utils.RunCmd(["umount", mpath])
368
      result = utils.RunCmd(timeout_cmd.extend(["umount", mpath]))
362 369
      if result.failed:
363 370
        logging.warning("Error while umounting subpath %s for instance %s: %s",
364 371
                        mpath, name, result.output)
365 372

  
366
    result = utils.RunCmd(["umount", root_dir])
373
    result = utils.RunCmd(timeout_cmd.extend(["umount", root_dir]))
367 374
    if result.failed and force:
368 375
      msg = ("Processes still alive in the chroot: %s" %
369 376
             utils.RunCmd("fuser -vm %s" % root_dir).output)
b/lib/hypervisor/hv_xen.py
519 519
                                   (instance.name, result.fail_reason,
520 520
                                    result.output, stashed_config))
521 521

  
522
  def StopInstance(self, instance, force=False, retry=False, name=None):
522
  def StopInstance(self, instance, force=False, retry=False, name=None,
523
                   timeout=None):
523 524
    """Stop an instance.
524 525

  
526
    A soft shutdown can be interrupted. A hard shutdown tries forever.
527

  
525 528
    """
529
    assert(timeout is None or force is not None)
530

  
526 531
    if name is None:
527 532
      name = instance.name
528 533

  
529
    return self._StopInstance(name, force)
534
    return self._StopInstance(name, force, timeout)
530 535

  
531
  def _ShutdownInstance(self, name):
536
  def _ShutdownInstance(self, name, timeout):
532 537
    """Shutdown an instance if the instance is running.
533 538

  
534 539
    The '-w' flag waits for shutdown to complete which avoids the need
......
537 542

  
538 543
    @type name: string
539 544
    @param name: name of the instance to stop
545
    @type timeout: int or None
546
    @param timeout: a timeout after which the shutdown command should be killed,
547
                    or None for no timeout
540 548

  
541 549
    """
542 550
    instance_info = self.GetInstanceInfo(name)
......
545 553
      logging.info("Failed to shutdown instance %s, not running", name)
546 554
      return None
547 555

  
548
    return self._RunXen(["shutdown", "-w", name])
556
    return self._RunXen(["shutdown", "-w", name], timeout)
549 557

  
550 558
  def _DestroyInstance(self, name):
551 559
    """Destroy an instance if the instance if the instance exists.
......
562 570

  
563 571
    return self._RunXen(["destroy", name])
564 572

  
565
  def _StopInstance(self, name, force):
573
  def _StopInstance(self, name, force, timeout):
566 574
    """Stop an instance.
567 575

  
568 576
    @type name: string
......
571 579
    @type force: boolean
572 580
    @param force: whether to do a "hard" stop (destroy)
573 581

  
582
    @type timeout: int or None
583
    @param timeout: a timeout after which the shutdown command should be killed,
584
                    or None for no timeout
585

  
574 586
    """
575 587
    if force:
576 588
      result = self._DestroyInstance(name)
577 589
    else:
578
      self._ShutdownInstance(name)
590
      self._ShutdownInstance(name, timeout)
579 591
      result = self._DestroyInstance(name)
580 592

  
581 593
    if result is not None and result.failed and \
b/test/py/ganeti.hypervisor.hv_xen_unittest.py
555 555
          extra = inst.hvparams[constants.HV_KERNEL_ARGS]
556 556
          self.assertTrue(("extra = '%s'" % extra) in lines)
557 557

  
558
  def _StopInstanceCommand(self, instance_name, force, fail, cmd):
558
  def _StopInstanceCommand(self, instance_name, force, fail, full_cmd):
559
    # Remove the timeout (and its number of seconds) if it's there
560
    if full_cmd[:1][0] == "timeout":
561
      cmd = full_cmd[2:]
562
    else:
563
      cmd = full_cmd
564

  
565
    # Test the actual command
559 566
    if (cmd == [self.CMD, "list"]):
560 567
      output = "Name  ID  Mem  VCPUs  State  Time(s)\n" \
561 568
        "Domain-0  0  1023  1  r-----  142691.0\n" \
......
592 599

  
593 600
        if fail:
594 601
          try:
595
            hv._StopInstance(name, force)
602
            hv._StopInstance(name, force, constants.DEFAULT_SHUTDOWN_TIMEOUT)
596 603
          except errors.HypervisorError, err:
597 604
            self.assertTrue(str(err).startswith("xm list failed"),
598 605
                            msg=str(err))
......
602 609
                           msg=("Configuration was removed when stopping"
603 610
                                " instance failed"))
604 611
        else:
605
          hv._StopInstance(name, force)
612
          hv._StopInstance(name, force, constants.DEFAULT_SHUTDOWN_TIMEOUT)
606 613
          self.assertFalse(os.path.exists(cfgfile))
607 614

  
608 615
  def _MigrateNonRunningInstCmd(self, cmd):

Also available in: Unified diff