4 # Copyright (C) 2012, 2014 Google Inc.
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.
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.
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
22 """Job-related QA tests.
26 from ganeti.utils import retry
27 from ganeti import constants
28 from ganeti import query
37 from qa_utils import AssertCommand, GetCommandOutput
42 qa_utils.GenericQueryTest("gnt-job", query.JOB_FIELDS.keys(),
43 namefield="id", test_unknown=False)
46 def TestJobListFields():
47 """gnt-node list-fields"""
48 qa_utils.GenericQueryFieldsTest("gnt-job", query.JOB_FIELDS.keys())
51 def _GetJobStatuses():
52 """ Invokes gnt-job list and extracts an id to status dictionary.
54 @rtype: dict of string to string
55 @return: A dictionary mapping job ids to matching statuses
58 master = qa_config.GetMasterNode()
59 list_output = GetCommandOutput(
60 master.primary, "gnt-job list --no-headers --output=id,status"
62 return dict(map(lambda s: s.split(), list_output.splitlines()))
65 def _GetJobStatus(job_id):
66 """ Retrieves the status of a job.
69 @param job_id: The job id, represented as a string.
70 @rtype: string or None
72 @return: The job status, or None if not present.
75 return _GetJobStatuses().get(job_id, None)
78 def _RetryingFetchJobStatus(retry_status, job_id):
79 """ Used with C{retry.Retry}, waits for a status other than the one given.
81 @type retry_status: string
82 @param retry_status: The old job status, expected to change.
84 @param job_id: The job id, represented as a string.
86 @rtype: string or None
87 @return: The new job status, or None if none could be retrieved.
90 status = _GetJobStatus(job_id)
91 if status == retry_status:
92 raise retry.RetryAgain()
96 def TestJobCancellation():
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)])
105 SECOND_COMMAND_DELAY = 1.0
106 master = qa_config.GetMasterNode()
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
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")
118 job_id = possible_job_ids[0]
119 AssertCommand(["gnt-job", "cancel", job_id])
121 # Now wait until the second job finishes, and expect the watch to fail due to
123 AssertCommand(["gnt-job", "watch", job_id], fail=True)
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)
134 job_status = retry.Retry(retry_fn, 2.0, 2 * FIRST_COMMAND_DELAY)
135 except retry.RetryTimeout:
136 # The job status remains the same
139 if job_status != constants.JOB_STATUS_CANCELED:
140 raise qa_error.Error("Job was not successfully cancelled, status "
141 "found: %s" % job_status)