Statistics
| Branch: | Tag: | Revision:

root / qa / qa_job.py @ e4f485cc

History | View | Annotate | Download (4.3 kB)

1
#
2
#
3

    
4
# Copyright (C) 2012, 2014 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Job-related QA tests.
23

24
"""
25

    
26
from ganeti.utils import retry
27
from ganeti import constants
28
from ganeti import query
29

    
30
import functools
31
import re
32

    
33
import qa_config
34
import qa_error
35
import qa_utils
36

    
37
from qa_utils import AssertCommand, GetCommandOutput
38

    
39

    
40
def TestJobList():
41
  """gnt-job list"""
42
  qa_utils.GenericQueryTest("gnt-job", query.JOB_FIELDS.keys(),
43
                            namefield="id", test_unknown=False)
44

    
45

    
46
def TestJobListFields():
47
  """gnt-node list-fields"""
48
  qa_utils.GenericQueryFieldsTest("gnt-job", query.JOB_FIELDS.keys())
49

    
50

    
51
def _GetJobStatuses():
52
  """ Invokes gnt-job list and extracts an id to status dictionary.
53

54
  @rtype: dict of string to string
55
  @return: A dictionary mapping job ids to matching statuses
56

57
  """
58
  master = qa_config.GetMasterNode()
59
  list_output = GetCommandOutput(
60
    master.primary, "gnt-job list --no-headers --output=id,status"
61
  )
62
  return dict(map(lambda s: s.split(), list_output.splitlines()))
63

    
64

    
65
def _GetJobStatus(job_id):
66
  """ Retrieves the status of a job.
67

68
  @type job_id: string
69
  @param job_id: The job id, represented as a string.
70
  @rtype: string or None
71

72
  @return: The job status, or None if not present.
73

74
  """
75
  return _GetJobStatuses().get(job_id, None)
76

    
77

    
78
def _RetryingFetchJobStatus(retry_status, job_id):
79
  """ Used with C{retry.Retry}, waits for a status other than the one given.
80

81
  @type retry_status: string
82
  @param retry_status: The old job status, expected to change.
83
  @type job_id: string
84
  @param job_id: The job id, represented as a string.
85

86
  @rtype: string or None
87
  @return: The new job status, or None if none could be retrieved.
88

89
  """
90
  status = _GetJobStatus(job_id)
91
  if status == retry_status:
92
    raise retry.RetryAgain()
93
  return status
94

    
95

    
96
def TestJobCancellation():
97
  """gnt-job cancel"""
98
  # The delay used for the first command should be large enough for the next
99
  # command and the cancellation command to complete before the first job is
100
  # done. The second delay should be small enough that not too much time is
101
  # spend waiting in the case of a failed cancel and a running command.
102
  FIRST_COMMAND_DELAY = 10.0
103
  AssertCommand(["gnt-debug", "delay", "--submit", str(FIRST_COMMAND_DELAY)])
104

    
105
  SECOND_COMMAND_DELAY = 1.0
106
  master = qa_config.GetMasterNode()
107

    
108
  # Forcing tty usage does not work on buildbot, so force all output of this
109
  # command to be redirected to stdout
110
  job_id_output = GetCommandOutput(
111
    master.primary, "gnt-debug delay --submit %s 2>&1" % SECOND_COMMAND_DELAY
112
  )
113

    
114
  possible_job_ids = re.findall("JobID: ([0-9]+)", job_id_output)
115
  if len(possible_job_ids) != 1:
116
    raise qa_error.Error("Cannot parse gnt-debug delay output to find job id")
117

    
118
  job_id = possible_job_ids[0]
119
  AssertCommand(["gnt-job", "cancel", job_id])
120

    
121
  # Now wait until the second job finishes, and expect the watch to fail due to
122
  # job cancellation
123
  AssertCommand(["gnt-job", "watch", job_id], fail=True)
124

    
125
  # Then check for job cancellation
126
  job_status = _GetJobStatus(job_id)
127
  if job_status != constants.JOB_STATUS_CANCELED:
128
    # Try and see if the job is being cancelled, and wait until the status
129
    # changes or we hit a timeout
130
    if job_status == constants.JOB_STATUS_CANCELING:
131
      retry_fn = functools.partial(_RetryingFetchJobStatus,
132
                                   constants.JOB_STATUS_CANCELING, job_id)
133
      try:
134
        job_status = retry.Retry(retry_fn, 2.0, 2 * FIRST_COMMAND_DELAY)
135
      except retry.RetryTimeout:
136
        # The job status remains the same
137
        pass
138

    
139
    if job_status != constants.JOB_STATUS_CANCELED:
140
      raise qa_error.Error("Job was not successfully cancelled, status "
141
                           "found: %s" % job_status)