root / qa / qa_job.py @ 31d3b918
History | View | Annotate | Download (4.3 kB)
1 | 09470dd8 | Michael Hanselmann | #
|
---|---|---|---|
2 | 09470dd8 | Michael Hanselmann | #
|
3 | 09470dd8 | Michael Hanselmann | |
4 | 66cb789f | Hrvoje Ribicic | # Copyright (C) 2012, 2014 Google Inc.
|
5 | 09470dd8 | Michael Hanselmann | #
|
6 | 09470dd8 | Michael Hanselmann | # This program is free software; you can redistribute it and/or modify
|
7 | 09470dd8 | Michael Hanselmann | # it under the terms of the GNU General Public License as published by
|
8 | 09470dd8 | Michael Hanselmann | # the Free Software Foundation; either version 2 of the License, or
|
9 | 09470dd8 | Michael Hanselmann | # (at your option) any later version.
|
10 | 09470dd8 | Michael Hanselmann | #
|
11 | 09470dd8 | Michael Hanselmann | # This program is distributed in the hope that it will be useful, but
|
12 | 09470dd8 | Michael Hanselmann | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 09470dd8 | Michael Hanselmann | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | 09470dd8 | Michael Hanselmann | # General Public License for more details.
|
15 | 09470dd8 | Michael Hanselmann | #
|
16 | 09470dd8 | Michael Hanselmann | # You should have received a copy of the GNU General Public License
|
17 | 09470dd8 | Michael Hanselmann | # along with this program; if not, write to the Free Software
|
18 | 09470dd8 | Michael Hanselmann | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | 09470dd8 | Michael Hanselmann | # 02110-1301, USA.
|
20 | 09470dd8 | Michael Hanselmann | |
21 | 09470dd8 | Michael Hanselmann | |
22 | 09470dd8 | Michael Hanselmann | """Job-related QA tests.
|
23 | 09470dd8 | Michael Hanselmann |
|
24 | 09470dd8 | Michael Hanselmann | """
|
25 | 09470dd8 | Michael Hanselmann | |
26 | e4f485cc | Hrvoje Ribicic | from ganeti.utils import retry |
27 | 66cb789f | Hrvoje Ribicic | from ganeti import constants |
28 | 09470dd8 | Michael Hanselmann | from ganeti import query |
29 | 09470dd8 | Michael Hanselmann | |
30 | e4f485cc | Hrvoje Ribicic | import functools |
31 | 66cb789f | Hrvoje Ribicic | import re |
32 | 66cb789f | Hrvoje Ribicic | |
33 | 66cb789f | Hrvoje Ribicic | import qa_config |
34 | 66cb789f | Hrvoje Ribicic | import qa_error |
35 | 09470dd8 | Michael Hanselmann | import qa_utils |
36 | 09470dd8 | Michael Hanselmann | |
37 | 66cb789f | Hrvoje Ribicic | from qa_utils import AssertCommand, GetCommandOutput |
38 | 66cb789f | Hrvoje Ribicic | |
39 | 09470dd8 | Michael Hanselmann | |
40 | 09470dd8 | Michael Hanselmann | def TestJobList(): |
41 | 09470dd8 | Michael Hanselmann | """gnt-job list"""
|
42 | 6d1e4845 | Michael Hanselmann | qa_utils.GenericQueryTest("gnt-job", query.JOB_FIELDS.keys(),
|
43 | 6d1e4845 | Michael Hanselmann | namefield="id", test_unknown=False) |
44 | 09470dd8 | Michael Hanselmann | |
45 | 09470dd8 | Michael Hanselmann | |
46 | 09470dd8 | Michael Hanselmann | def TestJobListFields(): |
47 | 09470dd8 | Michael Hanselmann | """gnt-node list-fields"""
|
48 | 09470dd8 | Michael Hanselmann | qa_utils.GenericQueryFieldsTest("gnt-job", query.JOB_FIELDS.keys())
|
49 | 66cb789f | Hrvoje Ribicic | |
50 | 66cb789f | Hrvoje Ribicic | |
51 | 66cb789f | Hrvoje Ribicic | def _GetJobStatuses(): |
52 | 66cb789f | Hrvoje Ribicic | """ Invokes gnt-job list and extracts an id to status dictionary.
|
53 | 66cb789f | Hrvoje Ribicic |
|
54 | 66cb789f | Hrvoje Ribicic | @rtype: dict of string to string
|
55 | 66cb789f | Hrvoje Ribicic | @return: A dictionary mapping job ids to matching statuses
|
56 | 66cb789f | Hrvoje Ribicic |
|
57 | 66cb789f | Hrvoje Ribicic | """
|
58 | 66cb789f | Hrvoje Ribicic | master = qa_config.GetMasterNode() |
59 | 66cb789f | Hrvoje Ribicic | list_output = GetCommandOutput( |
60 | 66cb789f | Hrvoje Ribicic | master.primary, "gnt-job list --no-headers --output=id,status"
|
61 | 66cb789f | Hrvoje Ribicic | ) |
62 | 66cb789f | Hrvoje Ribicic | return dict(map(lambda s: s.split(), list_output.splitlines())) |
63 | 66cb789f | Hrvoje Ribicic | |
64 | 66cb789f | Hrvoje Ribicic | |
65 | e4f485cc | Hrvoje Ribicic | def _GetJobStatus(job_id): |
66 | e4f485cc | Hrvoje Ribicic | """ Retrieves the status of a job.
|
67 | e4f485cc | Hrvoje Ribicic |
|
68 | e4f485cc | Hrvoje Ribicic | @type job_id: string
|
69 | e4f485cc | Hrvoje Ribicic | @param job_id: The job id, represented as a string.
|
70 | e4f485cc | Hrvoje Ribicic | @rtype: string or None
|
71 | e4f485cc | Hrvoje Ribicic |
|
72 | e4f485cc | Hrvoje Ribicic | @return: The job status, or None if not present.
|
73 | e4f485cc | Hrvoje Ribicic |
|
74 | e4f485cc | Hrvoje Ribicic | """
|
75 | e4f485cc | Hrvoje Ribicic | return _GetJobStatuses().get(job_id, None) |
76 | e4f485cc | Hrvoje Ribicic | |
77 | e4f485cc | Hrvoje Ribicic | |
78 | e4f485cc | Hrvoje Ribicic | def _RetryingFetchJobStatus(retry_status, job_id): |
79 | e4f485cc | Hrvoje Ribicic | """ Used with C{retry.Retry}, waits for a status other than the one given.
|
80 | e4f485cc | Hrvoje Ribicic |
|
81 | e4f485cc | Hrvoje Ribicic | @type retry_status: string
|
82 | e4f485cc | Hrvoje Ribicic | @param retry_status: The old job status, expected to change.
|
83 | e4f485cc | Hrvoje Ribicic | @type job_id: string
|
84 | e4f485cc | Hrvoje Ribicic | @param job_id: The job id, represented as a string.
|
85 | e4f485cc | Hrvoje Ribicic |
|
86 | e4f485cc | Hrvoje Ribicic | @rtype: string or None
|
87 | e4f485cc | Hrvoje Ribicic | @return: The new job status, or None if none could be retrieved.
|
88 | e4f485cc | Hrvoje Ribicic |
|
89 | e4f485cc | Hrvoje Ribicic | """
|
90 | e4f485cc | Hrvoje Ribicic | status = _GetJobStatus(job_id) |
91 | e4f485cc | Hrvoje Ribicic | if status == retry_status:
|
92 | e4f485cc | Hrvoje Ribicic | raise retry.RetryAgain()
|
93 | e4f485cc | Hrvoje Ribicic | return status
|
94 | e4f485cc | Hrvoje Ribicic | |
95 | e4f485cc | Hrvoje Ribicic | |
96 | 66cb789f | Hrvoje Ribicic | def TestJobCancellation(): |
97 | 66cb789f | Hrvoje Ribicic | """gnt-job cancel"""
|
98 | 66cb789f | Hrvoje Ribicic | # The delay used for the first command should be large enough for the next
|
99 | 66cb789f | Hrvoje Ribicic | # command and the cancellation command to complete before the first job is
|
100 | 66cb789f | Hrvoje Ribicic | # done. The second delay should be small enough that not too much time is
|
101 | 66cb789f | Hrvoje Ribicic | # spend waiting in the case of a failed cancel and a running command.
|
102 | 66cb789f | Hrvoje Ribicic | FIRST_COMMAND_DELAY = 10.0
|
103 | 66cb789f | Hrvoje Ribicic | AssertCommand(["gnt-debug", "delay", "--submit", str(FIRST_COMMAND_DELAY)]) |
104 | 66cb789f | Hrvoje Ribicic | |
105 | 66cb789f | Hrvoje Ribicic | SECOND_COMMAND_DELAY = 1.0
|
106 | 66cb789f | Hrvoje Ribicic | master = qa_config.GetMasterNode() |
107 | 66cb789f | Hrvoje Ribicic | |
108 | 66cb789f | Hrvoje Ribicic | # Forcing tty usage does not work on buildbot, so force all output of this
|
109 | 66cb789f | Hrvoje Ribicic | # command to be redirected to stdout
|
110 | 66cb789f | Hrvoje Ribicic | job_id_output = GetCommandOutput( |
111 | 66cb789f | Hrvoje Ribicic | master.primary, "gnt-debug delay --submit %s 2>&1" % SECOND_COMMAND_DELAY
|
112 | 66cb789f | Hrvoje Ribicic | ) |
113 | 66cb789f | Hrvoje Ribicic | |
114 | 66cb789f | Hrvoje Ribicic | possible_job_ids = re.findall("JobID: ([0-9]+)", job_id_output)
|
115 | 66cb789f | Hrvoje Ribicic | if len(possible_job_ids) != 1: |
116 | 66cb789f | Hrvoje Ribicic | raise qa_error.Error("Cannot parse gnt-debug delay output to find job id") |
117 | 66cb789f | Hrvoje Ribicic | |
118 | 66cb789f | Hrvoje Ribicic | job_id = possible_job_ids[0]
|
119 | 66cb789f | Hrvoje Ribicic | AssertCommand(["gnt-job", "cancel", job_id]) |
120 | 66cb789f | Hrvoje Ribicic | |
121 | 66cb789f | Hrvoje Ribicic | # Now wait until the second job finishes, and expect the watch to fail due to
|
122 | 66cb789f | Hrvoje Ribicic | # job cancellation
|
123 | 66cb789f | Hrvoje Ribicic | AssertCommand(["gnt-job", "watch", job_id], fail=True) |
124 | 66cb789f | Hrvoje Ribicic | |
125 | 66cb789f | Hrvoje Ribicic | # Then check for job cancellation
|
126 | e4f485cc | Hrvoje Ribicic | job_status = _GetJobStatus(job_id) |
127 | e4f485cc | Hrvoje Ribicic | if job_status != constants.JOB_STATUS_CANCELED:
|
128 | e4f485cc | Hrvoje Ribicic | # Try and see if the job is being cancelled, and wait until the status
|
129 | e4f485cc | Hrvoje Ribicic | # changes or we hit a timeout
|
130 | e4f485cc | Hrvoje Ribicic | if job_status == constants.JOB_STATUS_CANCELING:
|
131 | e4f485cc | Hrvoje Ribicic | retry_fn = functools.partial(_RetryingFetchJobStatus, |
132 | e4f485cc | Hrvoje Ribicic | constants.JOB_STATUS_CANCELING, job_id) |
133 | e4f485cc | Hrvoje Ribicic | try:
|
134 | e4f485cc | Hrvoje Ribicic | job_status = retry.Retry(retry_fn, 2.0, 2 * FIRST_COMMAND_DELAY) |
135 | e4f485cc | Hrvoje Ribicic | except retry.RetryTimeout:
|
136 | e4f485cc | Hrvoje Ribicic | # The job status remains the same
|
137 | e4f485cc | Hrvoje Ribicic | pass
|
138 | e4f485cc | Hrvoje Ribicic | |
139 | e4f485cc | Hrvoje Ribicic | if job_status != constants.JOB_STATUS_CANCELED:
|
140 | e4f485cc | Hrvoje Ribicic | raise qa_error.Error("Job was not successfully cancelled, status " |
141 | e4f485cc | Hrvoje Ribicic | "found: %s" % job_status) |