root / qa / qa_utils.py @ 5d831182
History | View | Annotate | Download (6.1 kB)
1 | c68d1f43 | Michael Hanselmann | #
|
---|---|---|---|
2 | c68d1f43 | Michael Hanselmann | #
|
3 | c68d1f43 | Michael Hanselmann | |
4 | cec9845c | Michael Hanselmann | # Copyright (C) 2007 Google Inc.
|
5 | cec9845c | Michael Hanselmann | #
|
6 | cec9845c | Michael Hanselmann | # This program is free software; you can redistribute it and/or modify
|
7 | cec9845c | Michael Hanselmann | # it under the terms of the GNU General Public License as published by
|
8 | cec9845c | Michael Hanselmann | # the Free Software Foundation; either version 2 of the License, or
|
9 | cec9845c | Michael Hanselmann | # (at your option) any later version.
|
10 | cec9845c | Michael Hanselmann | #
|
11 | cec9845c | Michael Hanselmann | # This program is distributed in the hope that it will be useful, but
|
12 | cec9845c | Michael Hanselmann | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | cec9845c | Michael Hanselmann | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | cec9845c | Michael Hanselmann | # General Public License for more details.
|
15 | cec9845c | Michael Hanselmann | #
|
16 | cec9845c | Michael Hanselmann | # You should have received a copy of the GNU General Public License
|
17 | cec9845c | Michael Hanselmann | # along with this program; if not, write to the Free Software
|
18 | cec9845c | Michael Hanselmann | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | cec9845c | Michael Hanselmann | # 02110-1301, USA.
|
20 | cec9845c | Michael Hanselmann | |
21 | cec9845c | Michael Hanselmann | |
22 | cec9845c | Michael Hanselmann | """Utilities for QA tests.
|
23 | cec9845c | Michael Hanselmann |
|
24 | cec9845c | Michael Hanselmann | """
|
25 | cec9845c | Michael Hanselmann | |
26 | cec9845c | Michael Hanselmann | import os |
27 | e6ce18ac | René Nussbaumer | import re |
28 | 23269c5b | Michael Hanselmann | import sys |
29 | cec9845c | Michael Hanselmann | import subprocess |
30 | cec9845c | Michael Hanselmann | |
31 | cec9845c | Michael Hanselmann | from ganeti import utils |
32 | cec9845c | Michael Hanselmann | |
33 | cec9845c | Michael Hanselmann | import qa_config |
34 | cec9845c | Michael Hanselmann | import qa_error |
35 | cec9845c | Michael Hanselmann | |
36 | cec9845c | Michael Hanselmann | |
37 | 23269c5b | Michael Hanselmann | _INFO_SEQ = None
|
38 | 23269c5b | Michael Hanselmann | _WARNING_SEQ = None
|
39 | 23269c5b | Michael Hanselmann | _ERROR_SEQ = None
|
40 | 23269c5b | Michael Hanselmann | _RESET_SEQ = None
|
41 | 23269c5b | Michael Hanselmann | |
42 | 23269c5b | Michael Hanselmann | |
43 | 23269c5b | Michael Hanselmann | def _SetupColours(): |
44 | 23269c5b | Michael Hanselmann | """Initializes the colour constants.
|
45 | 23269c5b | Michael Hanselmann |
|
46 | 23269c5b | Michael Hanselmann | """
|
47 | 23269c5b | Michael Hanselmann | global _INFO_SEQ, _WARNING_SEQ, _ERROR_SEQ, _RESET_SEQ
|
48 | 23269c5b | Michael Hanselmann | |
49 | dfe11bad | Michael Hanselmann | # Don't use colours if stdout isn't a terminal
|
50 | dfe11bad | Michael Hanselmann | if not sys.stdout.isatty(): |
51 | dfe11bad | Michael Hanselmann | return
|
52 | dfe11bad | Michael Hanselmann | |
53 | 23269c5b | Michael Hanselmann | try:
|
54 | 23269c5b | Michael Hanselmann | import curses |
55 | 23269c5b | Michael Hanselmann | except ImportError: |
56 | 23269c5b | Michael Hanselmann | # Don't use colours if curses module can't be imported
|
57 | 23269c5b | Michael Hanselmann | return
|
58 | 23269c5b | Michael Hanselmann | |
59 | 23269c5b | Michael Hanselmann | curses.setupterm() |
60 | 23269c5b | Michael Hanselmann | |
61 | 23269c5b | Michael Hanselmann | _RESET_SEQ = curses.tigetstr("op")
|
62 | 23269c5b | Michael Hanselmann | |
63 | 23269c5b | Michael Hanselmann | setaf = curses.tigetstr("setaf")
|
64 | 23269c5b | Michael Hanselmann | _INFO_SEQ = curses.tparm(setaf, curses.COLOR_GREEN) |
65 | 23269c5b | Michael Hanselmann | _WARNING_SEQ = curses.tparm(setaf, curses.COLOR_YELLOW) |
66 | 23269c5b | Michael Hanselmann | _ERROR_SEQ = curses.tparm(setaf, curses.COLOR_RED) |
67 | 23269c5b | Michael Hanselmann | |
68 | 23269c5b | Michael Hanselmann | |
69 | 23269c5b | Michael Hanselmann | _SetupColours() |
70 | 23269c5b | Michael Hanselmann | |
71 | 23269c5b | Michael Hanselmann | |
72 | eaef8a05 | Michael Hanselmann | def AssertIn(item, sequence): |
73 | eaef8a05 | Michael Hanselmann | """Raises an error when item is not in sequence.
|
74 | eaef8a05 | Michael Hanselmann |
|
75 | eaef8a05 | Michael Hanselmann | """
|
76 | eaef8a05 | Michael Hanselmann | if item not in sequence: |
77 | eaef8a05 | Michael Hanselmann | raise qa_error.Error('%r not in %r' % (item, sequence)) |
78 | eaef8a05 | Michael Hanselmann | |
79 | eaef8a05 | Michael Hanselmann | |
80 | e8ae0c20 | Michael Hanselmann | def AssertEqual(first, second): |
81 | cec9845c | Michael Hanselmann | """Raises an error when values aren't equal.
|
82 | cec9845c | Michael Hanselmann |
|
83 | cec9845c | Michael Hanselmann | """
|
84 | cec9845c | Michael Hanselmann | if not first == second: |
85 | e8ae0c20 | Michael Hanselmann | raise qa_error.Error('%r == %r' % (first, second)) |
86 | e8ae0c20 | Michael Hanselmann | |
87 | e8ae0c20 | Michael Hanselmann | |
88 | e8ae0c20 | Michael Hanselmann | def AssertNotEqual(first, second): |
89 | e8ae0c20 | Michael Hanselmann | """Raises an error when values are equal.
|
90 | e8ae0c20 | Michael Hanselmann |
|
91 | e8ae0c20 | Michael Hanselmann | """
|
92 | e8ae0c20 | Michael Hanselmann | if not first != second: |
93 | e8ae0c20 | Michael Hanselmann | raise qa_error.Error('%r != %r' % (first, second)) |
94 | cec9845c | Michael Hanselmann | |
95 | cec9845c | Michael Hanselmann | |
96 | e6ce18ac | René Nussbaumer | def AssertMatch(string, pattern): |
97 | e6ce18ac | René Nussbaumer | """Raises an error when string doesn't match regexp pattern.
|
98 | e6ce18ac | René Nussbaumer |
|
99 | e6ce18ac | René Nussbaumer | """
|
100 | e6ce18ac | René Nussbaumer | if not re.match(pattern, string): |
101 | e6ce18ac | René Nussbaumer | raise qa_error.Error("%r doesn't match /%r/" % (string, pattern)) |
102 | e6ce18ac | René Nussbaumer | |
103 | e6ce18ac | René Nussbaumer | |
104 | cec9845c | Michael Hanselmann | def GetSSHCommand(node, cmd, strict=True): |
105 | cec9845c | Michael Hanselmann | """Builds SSH command to be executed.
|
106 | cec9845c | Michael Hanselmann |
|
107 | c68d1f43 | Michael Hanselmann | Args:
|
108 | c68d1f43 | Michael Hanselmann | - node: Node the command should run on
|
109 | c68d1f43 | Michael Hanselmann | - cmd: Command to be executed as a list with all parameters
|
110 | c68d1f43 | Michael Hanselmann | - strict: Whether to enable strict host key checking
|
111 | c68d1f43 | Michael Hanselmann |
|
112 | cec9845c | Michael Hanselmann | """
|
113 | 305cb9bb | Michael Hanselmann | args = [ 'ssh', '-oEscapeChar=none', '-oBatchMode=yes', '-l', 'root', '-t' ] |
114 | cec9845c | Michael Hanselmann | |
115 | cec9845c | Michael Hanselmann | if strict:
|
116 | cec9845c | Michael Hanselmann | tmp = 'yes'
|
117 | cec9845c | Michael Hanselmann | else:
|
118 | cec9845c | Michael Hanselmann | tmp = 'no'
|
119 | cec9845c | Michael Hanselmann | args.append('-oStrictHostKeyChecking=%s' % tmp)
|
120 | cec9845c | Michael Hanselmann | args.append('-oClearAllForwardings=yes')
|
121 | cec9845c | Michael Hanselmann | args.append('-oForwardAgent=yes')
|
122 | cec9845c | Michael Hanselmann | args.append(node) |
123 | 26a61f87 | Michael Hanselmann | args.append(cmd) |
124 | cec9845c | Michael Hanselmann | |
125 | cec9845c | Michael Hanselmann | return args
|
126 | cec9845c | Michael Hanselmann | |
127 | cec9845c | Michael Hanselmann | |
128 | 5d831182 | Michael Hanselmann | def StartLocalCommand(cmd, **kwargs): |
129 | 5d831182 | Michael Hanselmann | """Starts a local command.
|
130 | 5d831182 | Michael Hanselmann |
|
131 | 5d831182 | Michael Hanselmann | """
|
132 | 5d831182 | Michael Hanselmann | print "Command: %s" % utils.ShellQuoteArgs(cmd) |
133 | 5d831182 | Michael Hanselmann | return subprocess.Popen(cmd, shell=False, **kwargs) |
134 | 5d831182 | Michael Hanselmann | |
135 | 5d831182 | Michael Hanselmann | |
136 | cec9845c | Michael Hanselmann | def StartSSH(node, cmd, strict=True): |
137 | cec9845c | Michael Hanselmann | """Starts SSH.
|
138 | cec9845c | Michael Hanselmann |
|
139 | cec9845c | Michael Hanselmann | """
|
140 | 5d831182 | Michael Hanselmann | return StartLocalCommand(GetSSHCommand(node, cmd, strict=strict))
|
141 | 4b62db14 | Michael Hanselmann | |
142 | 4b62db14 | Michael Hanselmann | |
143 | 4b62db14 | Michael Hanselmann | def GetCommandOutput(node, cmd): |
144 | 4b62db14 | Michael Hanselmann | """Returns the output of a command executed on the given node.
|
145 | 4b62db14 | Michael Hanselmann |
|
146 | 4b62db14 | Michael Hanselmann | """
|
147 | 5d831182 | Michael Hanselmann | p = StartLocalCommand(GetSSHCommand(node, cmd), stdout=subprocess.PIPE) |
148 | 4b62db14 | Michael Hanselmann | AssertEqual(p.wait(), 0)
|
149 | 4b62db14 | Michael Hanselmann | return p.stdout.read()
|
150 | cec9845c | Michael Hanselmann | |
151 | cec9845c | Michael Hanselmann | |
152 | cec9845c | Michael Hanselmann | def UploadFile(node, src): |
153 | cec9845c | Michael Hanselmann | """Uploads a file to a node and returns the filename.
|
154 | cec9845c | Michael Hanselmann |
|
155 | cec9845c | Michael Hanselmann | Caller needs to remove the returned file on the node when it's not needed
|
156 | cec9845c | Michael Hanselmann | anymore.
|
157 | 49d50e52 | Michael Hanselmann |
|
158 | cec9845c | Michael Hanselmann | """
|
159 | cec9845c | Michael Hanselmann | # Make sure nobody else has access to it while preserving local permissions
|
160 | cec9845c | Michael Hanselmann | mode = os.stat(src).st_mode & 0700
|
161 | cec9845c | Michael Hanselmann | |
162 | cec9845c | Michael Hanselmann | cmd = ('tmp=$(tempfile --mode %o --prefix gnt) && '
|
163 | cec9845c | Michael Hanselmann | '[[ -f "${tmp}" ]] && '
|
164 | cec9845c | Michael Hanselmann | 'cat > "${tmp}" && '
|
165 | cec9845c | Michael Hanselmann | 'echo "${tmp}"') % mode
|
166 | cec9845c | Michael Hanselmann | |
167 | cec9845c | Michael Hanselmann | f = open(src, 'r') |
168 | cec9845c | Michael Hanselmann | try:
|
169 | cec9845c | Michael Hanselmann | p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False, stdin=f,
|
170 | cec9845c | Michael Hanselmann | stdout=subprocess.PIPE) |
171 | cec9845c | Michael Hanselmann | AssertEqual(p.wait(), 0)
|
172 | cec9845c | Michael Hanselmann | |
173 | cec9845c | Michael Hanselmann | # Return temporary filename
|
174 | cec9845c | Michael Hanselmann | return p.stdout.read().strip()
|
175 | cec9845c | Michael Hanselmann | finally:
|
176 | cec9845c | Michael Hanselmann | f.close() |
177 | 5d640672 | Michael Hanselmann | |
178 | 5d640672 | Michael Hanselmann | |
179 | 49d50e52 | Michael Hanselmann | def BackupFile(node, path): |
180 | 49d50e52 | Michael Hanselmann | """Creates a backup of a file on the node and returns the filename.
|
181 | 49d50e52 | Michael Hanselmann |
|
182 | 49d50e52 | Michael Hanselmann | Caller needs to remove the returned file on the node when it's not needed
|
183 | 49d50e52 | Michael Hanselmann | anymore.
|
184 | 49d50e52 | Michael Hanselmann |
|
185 | 49d50e52 | Michael Hanselmann | """
|
186 | 49d50e52 | Michael Hanselmann | cmd = ("tmp=$(tempfile --prefix .gnt --directory=$(dirname %s)) && "
|
187 | 49d50e52 | Michael Hanselmann | "[[ -f \"$tmp\" ]] && "
|
188 | 49d50e52 | Michael Hanselmann | "cp %s $tmp && "
|
189 | 49d50e52 | Michael Hanselmann | "echo $tmp") % (utils.ShellQuote(path), utils.ShellQuote(path))
|
190 | 49d50e52 | Michael Hanselmann | |
191 | 49d50e52 | Michael Hanselmann | # Return temporary filename
|
192 | 49d50e52 | Michael Hanselmann | return GetCommandOutput(node, cmd).strip()
|
193 | 49d50e52 | Michael Hanselmann | |
194 | 49d50e52 | Michael Hanselmann | |
195 | 4b62db14 | Michael Hanselmann | def _ResolveName(cmd, key): |
196 | 4b62db14 | Michael Hanselmann | """Helper function.
|
197 | 4b62db14 | Michael Hanselmann |
|
198 | 4b62db14 | Michael Hanselmann | """
|
199 | 4b62db14 | Michael Hanselmann | master = qa_config.GetMasterNode() |
200 | 4b62db14 | Michael Hanselmann | |
201 | 4b62db14 | Michael Hanselmann | output = GetCommandOutput(master['primary'], utils.ShellQuoteArgs(cmd))
|
202 | 4b62db14 | Michael Hanselmann | for line in output.splitlines(): |
203 | 4b62db14 | Michael Hanselmann | (lkey, lvalue) = line.split(':', 1) |
204 | 4b62db14 | Michael Hanselmann | if lkey == key:
|
205 | 4b62db14 | Michael Hanselmann | return lvalue.lstrip()
|
206 | 4b62db14 | Michael Hanselmann | raise KeyError("Key not found") |
207 | 4b62db14 | Michael Hanselmann | |
208 | 4b62db14 | Michael Hanselmann | |
209 | 5d640672 | Michael Hanselmann | def ResolveInstanceName(instance): |
210 | 5d640672 | Michael Hanselmann | """Gets the full name of an instance.
|
211 | 5d640672 | Michael Hanselmann |
|
212 | 5d640672 | Michael Hanselmann | """
|
213 | e8ae0c20 | Michael Hanselmann | return _ResolveName(['gnt-instance', 'info', instance['name']], |
214 | 4b62db14 | Michael Hanselmann | 'Instance name')
|
215 | 4b62db14 | Michael Hanselmann | |
216 | 4b62db14 | Michael Hanselmann | |
217 | 4b62db14 | Michael Hanselmann | def ResolveNodeName(node): |
218 | 4b62db14 | Michael Hanselmann | """Gets the full name of a node.
|
219 | 4b62db14 | Michael Hanselmann |
|
220 | 4b62db14 | Michael Hanselmann | """
|
221 | 4b62db14 | Michael Hanselmann | return _ResolveName(['gnt-node', 'info', node['primary']], |
222 | 4b62db14 | Michael Hanselmann | 'Node name')
|
223 | 4b62db14 | Michael Hanselmann | |
224 | 4b62db14 | Michael Hanselmann | |
225 | 4b62db14 | Michael Hanselmann | def GetNodeInstances(node, secondaries=False): |
226 | 4b62db14 | Michael Hanselmann | """Gets a list of instances on a node.
|
227 | 4b62db14 | Michael Hanselmann |
|
228 | 4b62db14 | Michael Hanselmann | """
|
229 | 5d640672 | Michael Hanselmann | master = qa_config.GetMasterNode() |
230 | 4b62db14 | Michael Hanselmann | node_name = ResolveNodeName(node) |
231 | 5d640672 | Michael Hanselmann | |
232 | 4b62db14 | Michael Hanselmann | # Get list of all instances
|
233 | 4b62db14 | Michael Hanselmann | cmd = ['gnt-instance', 'list', '--separator=:', '--no-headers', |
234 | 4b62db14 | Michael Hanselmann | '--output=name,pnode,snodes']
|
235 | 4b62db14 | Michael Hanselmann | output = GetCommandOutput(master['primary'], utils.ShellQuoteArgs(cmd))
|
236 | 4b62db14 | Michael Hanselmann | |
237 | 4b62db14 | Michael Hanselmann | instances = [] |
238 | 4b62db14 | Michael Hanselmann | for line in output.splitlines(): |
239 | 4b62db14 | Michael Hanselmann | (name, pnode, snodes) = line.split(':', 2) |
240 | 4b62db14 | Michael Hanselmann | if ((not secondaries and pnode == node_name) or |
241 | 4b62db14 | Michael Hanselmann | (secondaries and node_name in snodes.split(','))): |
242 | 4b62db14 | Michael Hanselmann | instances.append(name) |
243 | 5d640672 | Michael Hanselmann | |
244 | 4b62db14 | Michael Hanselmann | return instances
|
245 | 23269c5b | Michael Hanselmann | |
246 | 23269c5b | Michael Hanselmann | |
247 | dfe11bad | Michael Hanselmann | def _FormatWithColor(text, seq): |
248 | dfe11bad | Michael Hanselmann | if not seq: |
249 | dfe11bad | Michael Hanselmann | return text
|
250 | dfe11bad | Michael Hanselmann | return "%s%s%s" % (seq, text, _RESET_SEQ) |
251 | 23269c5b | Michael Hanselmann | |
252 | 23269c5b | Michael Hanselmann | |
253 | dfe11bad | Michael Hanselmann | FormatWarning = lambda text: _FormatWithColor(text, _WARNING_SEQ)
|
254 | dfe11bad | Michael Hanselmann | FormatError = lambda text: _FormatWithColor(text, _ERROR_SEQ)
|
255 | dfe11bad | Michael Hanselmann | FormatInfo = lambda text: _FormatWithColor(text, _INFO_SEQ) |