Revision c9e05005

b/NEWS
23 23
- Deprecation warnings due to pycrypto/paramiko import in
24 24
  tools/setup-ssh have been silenced, as usually they are safe; please
25 25
  make sure to run an up-to-date paramiko version
26
- The QA scripts now depend on Python 2.5 or above
26 27

  
27 28

  
28 29
Version 2.5.0
b/qa/qa-sample.json
24 24
  "disk": ["1G", "512M"],
25 25
  "disk-growth": ["2G", "768M"],
26 26

  
27
  "# Script to check instance status": null,
28
  "instance-check": null,
29

  
27 30
  "nodes": [
28 31
    {
29 32
      "# Master node": null,
b/qa/qa_config.py
23 23

  
24 24
"""
25 25

  
26
import os
26 27

  
27 28
from ganeti import utils
28 29
from ganeti import serializer
......
31 32
import qa_error
32 33

  
33 34

  
35
_INSTANCE_CHECK_KEY = "instance-check"
36

  
37

  
34 38
cfg = None
35 39
options = None
36 40

  
......
55 59
    raise qa_error.Error("Config options 'disk' and 'disk-growth' must have"
56 60
                         " the same number of items")
57 61

  
62
  check = GetInstanceCheckScript()
63
  if check:
64
    try:
65
      os.stat(check)
66
    except EnvironmentError, err:
67
      raise qa_error.Error("Can't find instance check script '%s': %s" %
68
                           (check, err))
69

  
58 70

  
59 71
def get(name, default=None):
60 72
  return cfg.get(name, default)
......
135 147
                           tests, compat.all)
136 148

  
137 149

  
150
def GetInstanceCheckScript():
151
  """Returns path to instance check script or C{None}.
152

  
153
  """
154
  return cfg.get(_INSTANCE_CHECK_KEY, None)
155

  
156

  
138 157
def GetMasterNode():
139 158
  return cfg["nodes"][0]
140 159

  
b/qa/qa_utils.py
30 30
import random
31 31
import tempfile
32 32

  
33
try:
34
  import functools
35
except ImportError, err:
36
  raise ImportError("Python 2.5 or higher is required: %s" % err)
37

  
33 38
from ganeti import utils
34 39
from ganeti import compat
35 40
from ganeti import constants
41
from ganeti import ht
36 42

  
37 43
import qa_config
38 44
import qa_error
......
45 51

  
46 52
_MULTIPLEXERS = {}
47 53

  
54
#: Unique ID per QA run
55
_RUN_UUID = utils.NewUUID()
56

  
57

  
58
(INST_DOWN,
59
 INST_UP) = range(500, 502)
60

  
61
(FIRST_ARG,
62
 RETURN_VALUE) = range(1000, 1002)
63

  
48 64

  
49 65
def _SetupColours():
50 66
  """Initializes the colour constants.
......
522 538
                                              quoted_tmp_hosts))
523 539
  except qa_error.Error:
524 540
    AssertCommand(["rm", tmp_hosts])
541

  
542

  
543
def RunInstanceCheck(instance, running):
544
  """Check if instance is running or not.
545

  
546
  """
547
  script = qa_config.GetInstanceCheckScript()
548
  if not script:
549
    return
550

  
551
  master_node = qa_config.GetMasterNode()
552
  instance_name = instance["name"]
553

  
554
  # Build command to connect to master node
555
  master_ssh = GetSSHCommand(master_node["primary"], "--")
556

  
557
  if running:
558
    running_shellval = "1"
559
    running_text = ""
560
  else:
561
    running_shellval = ""
562
    running_text = "not "
563

  
564
  print FormatInfo("Checking if instance '%s' is %srunning" %
565
                   (instance_name, running_text))
566

  
567
  args = [script, instance_name]
568
  env = {
569
    "PATH": constants.HOOKS_PATH,
570
    "RUN_UUID": _RUN_UUID,
571
    "MASTER_SSH": utils.ShellQuoteArgs(master_ssh),
572
    "INSTANCE_NAME": instance_name,
573
    "INSTANCE_RUNNING": running_shellval,
574
    }
575

  
576
  result = os.spawnve(os.P_WAIT, script, args, env)
577
  if result != 0:
578
    raise qa_error.Error("Instance check failed with result %s" % result)
579

  
580

  
581
_TInstCheck = ht.TStrictDict(False, False, {
582
  "name": ht.TNonEmptyString,
583
  })
584

  
585

  
586
def _InstanceCheckInner(expected, instarg, args, result):
587
  """Helper function used by L{InstanceCheck}.
588

  
589
  """
590
  if instarg == FIRST_ARG:
591
    instance = args[0]
592
  elif instarg == RETURN_VALUE:
593
    instance = result
594
  else:
595
    raise Exception("Invalid value '%s' for instance argument" % instarg)
596

  
597
  if expected in (INST_DOWN, INST_UP):
598
    if not _TInstCheck(instance):
599
      raise Exception("Invalid instance: %s" % instance)
600

  
601
    RunInstanceCheck(instance, (expected == INST_UP))
602
  elif expected is not None:
603
    raise Exception("Invalid value '%s'" % expected)
604

  
605

  
606
def InstanceCheck(before, after, instarg):
607
  """Decorator to check instance status before and after test.
608

  
609
  @param before: L{INST_DOWN} if instance must be stopped before test,
610
    L{INST_UP} if instance must be running before test, L{None} to not check.
611
  @param after: L{INST_DOWN} if instance must be stopped after test,
612
    L{INST_UP} if instance must be running after test, L{None} to not check.
613
  @param instarg: L{FIRST_ARG} to use first argument to test as instance (a
614
    dictionary), L{RETURN_VALUE} to use return value (disallows pre-checks)
615

  
616
  """
617
  def decorator(fn):
618
    @functools.wraps(fn)
619
    def wrapper(*args, **kwargs):
620
      _InstanceCheckInner(before, instarg, args, NotImplemented)
621

  
622
      result = fn(*args, **kwargs)
623

  
624
      _InstanceCheckInner(after, instarg, args, result)
625

  
626
      return result
627
    return wrapper
628
  return decorator

Also available in: Unified diff