Merge lp:~javier.collado/utah/bug1178367 into lp:utah
- bug1178367
- Merge into dev
Proposed by
Javier Collado
Status: | Merged |
---|---|
Merged at revision: | 904 |
Proposed branch: | lp:~javier.collado/utah/bug1178367 |
Merge into: | lp:utah |
Diff against target: |
680 lines (+78/-348) 6 files modified
debian/changelog (+2/-0) debian/control (+1/-9) debian/rules (+1/-9) examples/run_utah_tests.py (+2/-108) tests/test_parser.py (+72/-0) utah/parser.py (+0/-222) |
To merge this branch: | bzr merge lp:~javier.collado/utah/bug1178367 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andy Doan (community) | Approve | ||
Review via email: mp+163339@code.launchpad.net |
Commit message
Description of the change
This branch fixes the parsing of the gigabytes option and adds a few test cases
to verify this.
Besides this, the `utah.parser` module and the `utah-parser` package are
removed since they aren't used anymore. The `utah.parser` module is now used to
have there the parser for the utah server script since that makes unit testing
easier.
To post a comment you must log in.
Revision history for this message
Andy Doan (doanac) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'debian/changelog' | |||
2 | --- debian/changelog 2013-05-09 23:36:07 +0000 | |||
3 | +++ debian/changelog 2013-05-10 16:56:25 +0000 | |||
4 | @@ -8,6 +8,8 @@ | |||
5 | 8 | * Apply retry strategy to install UTAH client package (LP: #1175732) | 8 | * Apply retry strategy to install UTAH client package (LP: #1175732) |
6 | 9 | * Added phoenix manpage (LP: #1067704) | 9 | * Added phoenix manpage (LP: #1067704) |
7 | 10 | * Apply all overrides in tslist.run to the test cases (LP: #1178238) | 10 | * Apply all overrides in tslist.run to the test cases (LP: #1178238) |
8 | 11 | * Set disksizes default directly instead of via argparse (LP: | ||
9 | 12 | #1178367) | ||
10 | 11 | 13 | ||
11 | 12 | -- Javier Collado <javier.collado@canonical.com> Tue, 07 May 2013 16:06:48 +0200 | 14 | -- Javier Collado <javier.collado@canonical.com> Tue, 07 May 2013 16:06:48 +0200 |
12 | 13 | 15 | ||
13 | 14 | 16 | ||
14 | === modified file 'debian/control' | |||
15 | --- debian/control 2013-05-08 14:45:47 +0000 | |||
16 | +++ debian/control 2013-05-10 16:56:25 +0000 | |||
17 | @@ -23,8 +23,7 @@ | |||
18 | 23 | 23 | ||
19 | 24 | Package: utah-all | 24 | Package: utah-all |
20 | 25 | Architecture: all | 25 | Architecture: all |
23 | 26 | Depends: ${misc:Depends}, ${python:Depends}, utah-bamboofeeder, utah-cobbler, | 26 | Depends: ${misc:Depends}, ${python:Depends}, utah-bamboofeeder, utah-cobbler |
22 | 27 | utah-parser | ||
24 | 28 | Description: Ubuntu Test Automation Harness Complete Package | 27 | Description: Ubuntu Test Automation Harness Complete Package |
25 | 29 | Automation framework for testing in Ubuntu, all sections | 28 | Automation framework for testing in Ubuntu, all sections |
26 | 30 | 29 | ||
27 | @@ -59,10 +58,3 @@ | |||
28 | 59 | Depends: ${misc:Depends}, ${python:Depends} | 58 | Depends: ${misc:Depends}, ${python:Depends} |
29 | 60 | Description: Ubuntu Test Automation Harness Common Files | 59 | Description: Ubuntu Test Automation Harness Common Files |
30 | 61 | Automation framework for testing in Ubuntu, common files | 60 | Automation framework for testing in Ubuntu, common files |
31 | 62 | |||
32 | 63 | Package: utah-parser | ||
33 | 64 | Architecture: all | ||
34 | 65 | Depends: ${misc:Depends}, ${python:Depends}, | ||
35 | 66 | python-jsonschema (>= 1.3~), python-yaml, utah-common | ||
36 | 67 | Description: Ubuntu Test Automation Harness Parser | ||
37 | 68 | Automation framework for testing in Ubuntu, result parser | ||
38 | 69 | 61 | ||
39 | === modified file 'debian/rules' | |||
40 | --- debian/rules 2013-05-08 13:28:00 +0000 | |||
41 | +++ debian/rules 2013-05-10 16:56:25 +0000 | |||
42 | @@ -34,18 +34,14 @@ | |||
43 | 34 | 34 | ||
44 | 35 | override_dh_auto_install: | 35 | override_dh_auto_install: |
45 | 36 | # utah and utah-client both live in the utah directory | 36 | # utah and utah-client both live in the utah directory |
46 | 37 | # utah-parser lives in that tree as well | ||
47 | 38 | # utah should only install the provisioning subdirectory, and utah-client should install everything else | 37 | # utah should only install the provisioning subdirectory, and utah-client should install everything else |
50 | 39 | # except parser.py, which is now its own package | 38 | # except __init__.py, which is now in the common package |
49 | 40 | # and __init__.py, which is now in the common package | ||
51 | 41 | # The baremetal subdirectory in the provisioning directory is now 3 packages | 39 | # The baremetal subdirectory in the provisioning directory is now 3 packages |
52 | 42 | # If we just use dh_auto_install, all packages will get everything | 40 | # If we just use dh_auto_install, all packages will get everything |
53 | 43 | # We start by building the whole thing | 41 | # We start by building the whole thing |
54 | 44 | set -e && for pyvers in $(PYVERS); do python$$pyvers setup.py install --install-layout=deb --root=$(CURDIR); done | 42 | set -e && for pyvers in $(PYVERS); do python$$pyvers setup.py install --install-layout=deb --root=$(CURDIR); done |
55 | 45 | # Next, we move the built provisioning directory aside for later use | 43 | # Next, we move the built provisioning directory aside for later use |
56 | 46 | mv build/*/utah/provisioning . | 44 | mv build/*/utah/provisioning . |
57 | 47 | # Same with parser.py | ||
58 | 48 | mv build/*/utah/parser.py . | ||
59 | 49 | # And __init__.py | 45 | # And __init__.py |
60 | 50 | mv build/*/utah/__init__.py . | 46 | mv build/*/utah/__init__.py . |
61 | 51 | # Now we install into the utah-client directory, since provisioning is removed, using --skip-build to not rebuild it | 47 | # Now we install into the utah-client directory, since provisioning is removed, using --skip-build to not rebuild it |
62 | @@ -58,10 +54,6 @@ | |||
63 | 58 | mv build/*/utah/provisioning/baremetal . | 54 | mv build/*/utah/provisioning/baremetal . |
64 | 59 | # Now we install just the provisioning directory, again using --skip-build into the utah package tree | 55 | # Now we install just the provisioning directory, again using --skip-build into the utah package tree |
65 | 60 | set -e && for pyvers in $(PYVERS); do python$$pyvers setup.py install --skip-build --install-layout=deb --root=$(CURDIR)/debian/utah; done | 56 | set -e && for pyvers in $(PYVERS); do python$$pyvers setup.py install --skip-build --install-layout=deb --root=$(CURDIR)/debian/utah; done |
66 | 61 | # And now we put back the parser and install that | ||
67 | 62 | rm -r build/*/utah/* | ||
68 | 63 | mv parser.py build/*/utah | ||
69 | 64 | set -e && for pyvers in $(PYVERS); do python$$pyvers setup.py install --skip-build --install-layout=deb --root=$(CURDIR)/debian/utah-parser; done | ||
70 | 65 | # And now we put back the init file and install that | 57 | # And now we put back the init file and install that |
71 | 66 | rm -r build/*/utah/* | 58 | rm -r build/*/utah/* |
72 | 67 | mv __init__.py build/*/utah | 59 | mv __init__.py build/*/utah |
73 | 68 | 60 | ||
74 | === modified file 'examples/run_utah_tests.py' | |||
75 | --- examples/run_utah_tests.py 2013-05-01 23:57:13 +0000 | |||
76 | +++ examples/run_utah_tests.py 2013-05-10 16:56:25 +0000 | |||
77 | @@ -17,8 +17,6 @@ | |||
78 | 17 | 17 | ||
79 | 18 | """Provision a machine a run a test.""" | 18 | """Provision a machine a run a test.""" |
80 | 19 | 19 | ||
81 | 20 | |||
82 | 21 | import argparse | ||
83 | 22 | import logging | 20 | import logging |
84 | 23 | import os | 21 | import os |
85 | 24 | import sys | 22 | import sys |
86 | @@ -31,16 +29,15 @@ | |||
87 | 31 | from utah.cleanup import cleanup | 29 | from utah.cleanup import cleanup |
88 | 32 | from utah.exceptions import UTAHException | 30 | from utah.exceptions import UTAHException |
89 | 33 | from utah.group import check_user_group, print_group_error_message | 31 | from utah.group import check_user_group, print_group_error_message |
90 | 32 | from utah.parser import get_parser, parse_args # NOQA | ||
91 | 34 | from utah.provisioning.ssh import ProvisionedMachine | 33 | from utah.provisioning.ssh import ProvisionedMachine |
92 | 35 | from utah.provisioning.vm import TinySQLiteInventory | 34 | from utah.provisioning.vm import TinySQLiteInventory |
93 | 36 | from utah.run import ( | 35 | from utah.run import ( |
94 | 37 | configure_logging, | 36 | configure_logging, |
95 | 38 | master_runlist_argument, | ||
96 | 39 | run_tests, | 37 | run_tests, |
97 | 40 | ReturnCodes, | 38 | ReturnCodes, |
98 | 41 | ) | 39 | ) |
99 | 42 | from utah.timeout import timeout, UTAHTimeout | 40 | from utah.timeout import timeout, UTAHTimeout |
100 | 43 | from utah.url import url_argument | ||
101 | 44 | 41 | ||
102 | 45 | 42 | ||
103 | 46 | MISSING = [] | 43 | MISSING = [] |
104 | @@ -59,108 +56,6 @@ | |||
105 | 59 | MISSING.append('baremetal') | 56 | MISSING.append('baremetal') |
106 | 60 | 57 | ||
107 | 61 | 58 | ||
108 | 62 | def get_parser(): | ||
109 | 63 | parser = argparse.ArgumentParser( | ||
110 | 64 | description=('Provision a machine ' | ||
111 | 65 | 'and run one or more UTAH runlists there.\n\n' | ||
112 | 66 | 'The appropriate run_*.py script will be called, ' | ||
113 | 67 | 'depending on the parameters passed.'), | ||
114 | 68 | epilog=("For example:\n" | ||
115 | 69 | "Provision a VM using a precise server image " | ||
116 | 70 | "with i386 architecture and run the given runlist\n" | ||
117 | 71 | "\t%(prog)s -s precise -t server -a i386 \\\n" | ||
118 | 72 | "\t\t/usr/share/utah/client/examples/master.run"), | ||
119 | 73 | formatter_class=argparse.RawDescriptionHelpFormatter) | ||
120 | 74 | parser.add_argument('-m', '--machinetype', metavar='MACHINETYPE', | ||
121 | 75 | choices=('physical', 'virtual'), | ||
122 | 76 | default=config.machinetype, | ||
123 | 77 | help='Type of machine to provision (%(choices)s) ' | ||
124 | 78 | '(Default is %(default)s)') | ||
125 | 79 | parser.add_argument('-v', '--variant', | ||
126 | 80 | default=config.variant, | ||
127 | 81 | help='Variant of architecture, i.e., armel, armhf') | ||
128 | 82 | parser.add_argument('--skip-provisioning', action='store_true', | ||
129 | 83 | help='Reuse a system that is already provisioned ' | ||
130 | 84 | '(name argument must be passed)') | ||
131 | 85 | parser.add_argument('runlist', metavar='runlist', | ||
132 | 86 | type=master_runlist_argument, | ||
133 | 87 | help='URLs of runlist files to run') | ||
134 | 88 | parser.add_argument('-s', '--series', metavar='SERIES', | ||
135 | 89 | choices=config.serieschoices, | ||
136 | 90 | default=config.series, | ||
137 | 91 | help='Series to use for installation (%(choices)s) ' | ||
138 | 92 | '(Default is %(default)s)') | ||
139 | 93 | parser.add_argument('-t', '--type', metavar='TYPE', | ||
140 | 94 | choices=('desktop', 'server', 'mini', 'alternate'), | ||
141 | 95 | default=config.installtype, | ||
142 | 96 | help='Install type to use for installation ' | ||
143 | 97 | '(%(choices)s) (Default is %(default)s)') | ||
144 | 98 | parser.add_argument('-a', '--arch', metavar='ARCH', | ||
145 | 99 | choices=('i386', 'amd64', 'arm'), | ||
146 | 100 | default=config.arch, | ||
147 | 101 | help='Architecture to use for installation ' | ||
148 | 102 | '(%(choices)s) (Default is %(default)s)') | ||
149 | 103 | parser.add_argument('-n', '--no-destroy', action='store_true', | ||
150 | 104 | help='Preserve VM after tests have run') | ||
151 | 105 | parser.add_argument('-d', '--debug', action='store_true', | ||
152 | 106 | help='Enable debug logging') | ||
153 | 107 | parser.add_argument('-j', '--json', action='store_true', | ||
154 | 108 | help='Enable json logging (default is YAML)') | ||
155 | 109 | parser.add_argument('-f', '--files', action='append', | ||
156 | 110 | help='File or directory to copy from test system ') | ||
157 | 111 | parser.add_argument('-o', '--outdir', | ||
158 | 112 | help=('Directory to store locally copied files ' | ||
159 | 113 | '(Default is {}/machine-name)' | ||
160 | 114 | .format(config.logpath))) | ||
161 | 115 | parser.add_argument('--dumplogs', action='store_true', | ||
162 | 116 | help='Write client output logs to standard out') | ||
163 | 117 | parser.add_argument('--outputpreseed', action='store_true', | ||
164 | 118 | help='Copy preseed to logs directory and list as ' | ||
165 | 119 | 'log file in output') | ||
166 | 120 | parser.add_argument('-i', '--image', type=url_argument, | ||
167 | 121 | default=config.image, | ||
168 | 122 | help='Image/ISO file to use for installation') | ||
169 | 123 | parser.add_argument('-p', '--preseed', type=url_argument, | ||
170 | 124 | default=config.preseed, | ||
171 | 125 | help='Preseed file to use for installation') | ||
172 | 126 | parser.add_argument('-b', '--boot', | ||
173 | 127 | default=config.boot, | ||
174 | 128 | help='Boot arguments for initial installation') | ||
175 | 129 | parser.add_argument('--rewrite', | ||
176 | 130 | choices=('all', 'minimal', 'casperonly', 'none'), | ||
177 | 131 | default=config.rewrite, | ||
178 | 132 | help='Set level of automatic configuration rewriting ' | ||
179 | 133 | '(Default is %(default)s)') | ||
180 | 134 | parser.add_argument('-k', '--kernel', type=url_argument, | ||
181 | 135 | default=config.kernel, | ||
182 | 136 | help='Kernel file to use for installation') | ||
183 | 137 | parser.add_argument('-r', '--initrd', type=url_argument, | ||
184 | 138 | default=config.initrd, | ||
185 | 139 | help='InitRD file to use for installation') | ||
186 | 140 | parser.add_argument('--name', | ||
187 | 141 | default=config.name, | ||
188 | 142 | help='Name of machine to provision') | ||
189 | 143 | parser.add_argument('-e', '--emulator', | ||
190 | 144 | default=config.emulator, | ||
191 | 145 | help='Emulator to use (kvm and qemu are supported, ' | ||
192 | 146 | 'kvm will be favored if available)') | ||
193 | 147 | parser.add_argument('-x', '--xml', type=url_argument, | ||
194 | 148 | default=config.xml, | ||
195 | 149 | help='XML VM definition file ' | ||
196 | 150 | '(Default is %(default)s)') | ||
197 | 151 | parser.add_argument('-g', '--gigabytes', action='append', | ||
198 | 152 | default=config.disksizes, | ||
199 | 153 | help='Size in gigabytes of virtual disk, ' | ||
200 | 154 | 'specify more than once for multiple disks ' | ||
201 | 155 | '(Default is %(default)s)') | ||
202 | 156 | parser.add_argument('--diskbus', metavar='DISKBUS', | ||
203 | 157 | choices=('virtio', 'sata', 'ide'), | ||
204 | 158 | default=config.diskbus, | ||
205 | 159 | help='Disk bus to use for customvm installation ' | ||
206 | 160 | '(%(choices)s) (Default is %(default)s)') | ||
207 | 161 | return parser | ||
208 | 162 | |||
209 | 163 | |||
210 | 164 | def _get_machine(args): | 59 | def _get_machine(args): |
211 | 165 | if args.skip_provisioning: | 60 | if args.skip_provisioning: |
212 | 166 | # TBD: Inventory should be used to verify machine | 61 | # TBD: Inventory should be used to verify machine |
213 | @@ -210,8 +105,7 @@ | |||
214 | 210 | 105 | ||
215 | 211 | 106 | ||
216 | 212 | def run_utah_tests(args=None): | 107 | def run_utah_tests(args=None): |
219 | 213 | if args is None: | 108 | args = parse_args() |
218 | 214 | args = get_parser().parse_args() | ||
220 | 215 | 109 | ||
221 | 216 | if not check_user_group(): | 110 | if not check_user_group(): |
222 | 217 | print_group_error_message(__file__) | 111 | print_group_error_message(__file__) |
223 | 218 | 112 | ||
224 | === added file 'tests/test_parser.py' | |||
225 | --- tests/test_parser.py 1970-01-01 00:00:00 +0000 | |||
226 | +++ tests/test_parser.py 2013-05-10 16:56:25 +0000 | |||
227 | @@ -0,0 +1,72 @@ | |||
228 | 1 | # Ubuntu Testing Automation Harness | ||
229 | 2 | # Copyright 2013 Canonical Ltd. | ||
230 | 3 | |||
231 | 4 | # This program is free software: you can redistribute it and/or modify it | ||
232 | 5 | # under the terms of the GNU General Public License version 3, as published | ||
233 | 6 | # by the Free Software Foundation. | ||
234 | 7 | |||
235 | 8 | # This program is distributed in the hope that it will be useful, but | ||
236 | 9 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
237 | 10 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
238 | 11 | # PURPOSE. See the GNU General Public License for more details. | ||
239 | 12 | |||
240 | 13 | # You should have received a copy of the GNU General Public License along | ||
241 | 14 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
242 | 15 | |||
243 | 16 | """Unit tests for the utah.parser module.""" | ||
244 | 17 | |||
245 | 18 | import unittest | ||
246 | 19 | |||
247 | 20 | from mock import patch, DEFAULT | ||
248 | 21 | |||
249 | 22 | from utah import config | ||
250 | 23 | from utah.parser import parse_args | ||
251 | 24 | |||
252 | 25 | |||
253 | 26 | class TestParser(unittest.TestCase): | ||
254 | 27 | |||
255 | 28 | """Tests that command line parsing works as expected.""" | ||
256 | 29 | |||
257 | 30 | def parse_args_wrapper(self, argv): | ||
258 | 31 | """A wrapper to ensure that runlist argument passes validation. | ||
259 | 32 | |||
260 | 33 | :param argv: Command line arguments to be tested | ||
261 | 34 | :type argv: list | ||
262 | 35 | :returns: Parse arguments | ||
263 | 36 | :rtype: `argparse.Namespace` | ||
264 | 37 | |||
265 | 38 | """ | ||
266 | 39 | with patch.multiple('utah.run', | ||
267 | 40 | url_argument=DEFAULT, | ||
268 | 41 | parse_yaml_file=DEFAULT) as patches: | ||
269 | 42 | patches['url_argument'].return_value = '<local_runlist_file>' | ||
270 | 43 | patches['parse_yaml_file'].return_value = { | ||
271 | 44 | 'testsuites': [ | ||
272 | 45 | {'name': 'test_suite_name', | ||
273 | 46 | 'fetch_method': 'dev', | ||
274 | 47 | 'fetch_location': '<fetch_location>', | ||
275 | 48 | }, | ||
276 | 49 | ] | ||
277 | 50 | } | ||
278 | 51 | return parse_args(argv) | ||
279 | 52 | |||
280 | 53 | def test_gigabytes_default(self): | ||
281 | 54 | """Verify that gigabytes is set to default value if not passed.""" | ||
282 | 55 | args = self.parse_args_wrapper(['<runlist_file>']) | ||
283 | 56 | self.assertEqual(args.gigabytes, config.disksizes) | ||
284 | 57 | |||
285 | 58 | def test_gigabytes_one_argument(self): | ||
286 | 59 | """Verify that gigabytes is set to the argument passed.""" | ||
287 | 60 | gigabytes = '3' | ||
288 | 61 | args = self.parse_args_wrapper(['--gigabytes', gigabytes, | ||
289 | 62 | '<runlist_file>']) | ||
290 | 63 | self.assertEqual(args.gigabytes, [gigabytes]) | ||
291 | 64 | |||
292 | 65 | def test_gigabytes_multiple_arguments(self): | ||
293 | 66 | """Verify that gigabytes is set to the arguments passed.""" | ||
294 | 67 | gigabytes = ['3', '5', '7'] | ||
295 | 68 | args = self.parse_args_wrapper(['--gigabytes', gigabytes[0], | ||
296 | 69 | '--gigabytes', gigabytes[1], | ||
297 | 70 | '--gigabytes', gigabytes[2], | ||
298 | 71 | '<runlist_file>']) | ||
299 | 72 | self.assertEqual(args.gigabytes, gigabytes) | ||
300 | 0 | 73 | ||
301 | === added file 'utah/parser.py' | |||
302 | --- utah/parser.py 1970-01-01 00:00:00 +0000 | |||
303 | +++ utah/parser.py 2013-05-10 16:56:25 +0000 | |||
304 | @@ -0,0 +1,149 @@ | |||
305 | 1 | # Ubuntu Testing Automation Harness | ||
306 | 2 | # Copyright 2012 Canonical Ltd. | ||
307 | 3 | |||
308 | 4 | # This program is free software: you can redistribute it and/or modify it | ||
309 | 5 | # under the terms of the GNU General Public License version 3, as published | ||
310 | 6 | # by the Free Software Foundation. | ||
311 | 7 | |||
312 | 8 | # This program is distributed in the hope that it will be useful, but | ||
313 | 9 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
314 | 10 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
315 | 11 | # PURPOSE. See the GNU General Public License for more details. | ||
316 | 12 | |||
317 | 13 | # You should have received a copy of the GNU General Public License along | ||
318 | 14 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
319 | 15 | |||
320 | 16 | """Command line parser for the utah server script.""" | ||
321 | 17 | |||
322 | 18 | import argparse | ||
323 | 19 | import sys | ||
324 | 20 | |||
325 | 21 | from utah import config | ||
326 | 22 | from utah.run import master_runlist_argument | ||
327 | 23 | from utah.url import url_argument | ||
328 | 24 | |||
329 | 25 | |||
330 | 26 | def parse_args(argv=None): | ||
331 | 27 | """Parse command line arguments. | ||
332 | 28 | |||
333 | 29 | :param argv: Command line arguments to be parsed | ||
334 | 30 | :type argv: list | ||
335 | 31 | :returns: Parse arguments | ||
336 | 32 | :rtype: `argparse.Namespace` | ||
337 | 33 | |||
338 | 34 | """ | ||
339 | 35 | |||
340 | 36 | if argv is None: | ||
341 | 37 | argv = sys.argv[1:] | ||
342 | 38 | |||
343 | 39 | args = get_parser().parse_args(argv) | ||
344 | 40 | if args.gigabytes is None: | ||
345 | 41 | args.gigabytes = config.disksizes | ||
346 | 42 | return args | ||
347 | 43 | |||
348 | 44 | |||
349 | 45 | def get_parser(): | ||
350 | 46 | """Get command line parser. | ||
351 | 47 | |||
352 | 48 | :returns: Parser object | ||
353 | 49 | :rtype: `argparse.ArgumentParser` | ||
354 | 50 | |||
355 | 51 | """ | ||
356 | 52 | parser = argparse.ArgumentParser( | ||
357 | 53 | description=('Provision a machine ' | ||
358 | 54 | 'and run one or more UTAH runlists there.\n\n' | ||
359 | 55 | 'The appropriate run_*.py script will be called, ' | ||
360 | 56 | 'depending on the parameters passed.'), | ||
361 | 57 | epilog=("For example:\n" | ||
362 | 58 | "Provision a VM using a precise server image " | ||
363 | 59 | "with i386 architecture and run the given runlist\n" | ||
364 | 60 | "\t%(prog)s -s precise -t server -a i386 \\\n" | ||
365 | 61 | "\t\t/usr/share/utah/client/examples/master.run"), | ||
366 | 62 | formatter_class=argparse.RawDescriptionHelpFormatter) | ||
367 | 63 | parser.add_argument('-m', '--machinetype', metavar='MACHINETYPE', | ||
368 | 64 | choices=('physical', 'virtual'), | ||
369 | 65 | default=config.machinetype, | ||
370 | 66 | help='Type of machine to provision (%(choices)s) ' | ||
371 | 67 | '(Default is %(default)s)') | ||
372 | 68 | parser.add_argument('-v', '--variant', | ||
373 | 69 | default=config.variant, | ||
374 | 70 | help='Variant of architecture, i.e., armel, armhf') | ||
375 | 71 | parser.add_argument('--skip-provisioning', action='store_true', | ||
376 | 72 | help='Reuse a system that is already provisioned ' | ||
377 | 73 | '(name argument must be passed)') | ||
378 | 74 | parser.add_argument('runlist', metavar='runlist', | ||
379 | 75 | type=master_runlist_argument, | ||
380 | 76 | help='URLs of runlist files to run') | ||
381 | 77 | parser.add_argument('-s', '--series', metavar='SERIES', | ||
382 | 78 | choices=config.serieschoices, | ||
383 | 79 | default=config.series, | ||
384 | 80 | help='Series to use for installation (%(choices)s) ' | ||
385 | 81 | '(Default is %(default)s)') | ||
386 | 82 | parser.add_argument('-t', '--type', metavar='TYPE', | ||
387 | 83 | choices=('desktop', 'server', 'mini', 'alternate'), | ||
388 | 84 | default=config.installtype, | ||
389 | 85 | help='Install type to use for installation ' | ||
390 | 86 | '(%(choices)s) (Default is %(default)s)') | ||
391 | 87 | parser.add_argument('-a', '--arch', metavar='ARCH', | ||
392 | 88 | choices=('i386', 'amd64', 'arm'), | ||
393 | 89 | default=config.arch, | ||
394 | 90 | help='Architecture to use for installation ' | ||
395 | 91 | '(%(choices)s) (Default is %(default)s)') | ||
396 | 92 | parser.add_argument('-n', '--no-destroy', action='store_true', | ||
397 | 93 | help='Preserve VM after tests have run') | ||
398 | 94 | parser.add_argument('-d', '--debug', action='store_true', | ||
399 | 95 | help='Enable debug logging') | ||
400 | 96 | parser.add_argument('-j', '--json', action='store_true', | ||
401 | 97 | help='Enable json logging (default is YAML)') | ||
402 | 98 | parser.add_argument('-f', '--files', action='append', | ||
403 | 99 | help='File or directory to copy from test system ') | ||
404 | 100 | parser.add_argument('-o', '--outdir', | ||
405 | 101 | help=('Directory to store locally copied files ' | ||
406 | 102 | '(Default is {}/machine-name)' | ||
407 | 103 | .format(config.logpath))) | ||
408 | 104 | parser.add_argument('--dumplogs', action='store_true', | ||
409 | 105 | help='Write client output logs to standard out') | ||
410 | 106 | parser.add_argument('--outputpreseed', action='store_true', | ||
411 | 107 | help='Copy preseed to logs directory and list as ' | ||
412 | 108 | 'log file in output') | ||
413 | 109 | parser.add_argument('-i', '--image', type=url_argument, | ||
414 | 110 | default=config.image, | ||
415 | 111 | help='Image/ISO file to use for installation') | ||
416 | 112 | parser.add_argument('-p', '--preseed', type=url_argument, | ||
417 | 113 | default=config.preseed, | ||
418 | 114 | help='Preseed file to use for installation') | ||
419 | 115 | parser.add_argument('-b', '--boot', | ||
420 | 116 | default=config.boot, | ||
421 | 117 | help='Boot arguments for initial installation') | ||
422 | 118 | parser.add_argument('--rewrite', | ||
423 | 119 | choices=('all', 'minimal', 'casperonly', 'none'), | ||
424 | 120 | default=config.rewrite, | ||
425 | 121 | help='Set level of automatic configuration rewriting ' | ||
426 | 122 | '(Default is %(default)s)') | ||
427 | 123 | parser.add_argument('-k', '--kernel', type=url_argument, | ||
428 | 124 | default=config.kernel, | ||
429 | 125 | help='Kernel file to use for installation') | ||
430 | 126 | parser.add_argument('-r', '--initrd', type=url_argument, | ||
431 | 127 | default=config.initrd, | ||
432 | 128 | help='InitRD file to use for installation') | ||
433 | 129 | parser.add_argument('--name', | ||
434 | 130 | default=config.name, | ||
435 | 131 | help='Name of machine to provision') | ||
436 | 132 | parser.add_argument('-e', '--emulator', | ||
437 | 133 | default=config.emulator, | ||
438 | 134 | help='Emulator to use (kvm and qemu are supported, ' | ||
439 | 135 | 'kvm will be favored if available)') | ||
440 | 136 | parser.add_argument('-x', '--xml', type=url_argument, | ||
441 | 137 | default=config.xml, | ||
442 | 138 | help='XML VM definition file ' | ||
443 | 139 | '(Default is %(default)s)') | ||
444 | 140 | parser.add_argument('-g', '--gigabytes', action='append', | ||
445 | 141 | help='Size in gigabytes of virtual disk, ' | ||
446 | 142 | 'specify more than once for multiple disks ' | ||
447 | 143 | '(Default is {})'.format(config.disksizes)) | ||
448 | 144 | parser.add_argument('--diskbus', metavar='DISKBUS', | ||
449 | 145 | choices=('virtio', 'sata', 'ide'), | ||
450 | 146 | default=config.diskbus, | ||
451 | 147 | help='Disk bus to use for customvm installation ' | ||
452 | 148 | '(%(choices)s) (Default is %(default)s)') | ||
453 | 149 | return parser | ||
454 | 0 | 150 | ||
455 | === removed file 'utah/parser.py' | |||
456 | --- utah/parser.py 2013-04-03 21:17:26 +0000 | |||
457 | +++ utah/parser.py 1970-01-01 00:00:00 +0000 | |||
458 | @@ -1,222 +0,0 @@ | |||
459 | 1 | # Ubuntu Testing Automation Harness | ||
460 | 2 | # Copyright 2012 Canonical Ltd. | ||
461 | 3 | |||
462 | 4 | # This program is free software: you can redistribute it and/or modify it | ||
463 | 5 | # under the terms of the GNU General Public License version 3, as published | ||
464 | 6 | # by the Free Software Foundation. | ||
465 | 7 | |||
466 | 8 | # This program is distributed in the hope that it will be useful, but | ||
467 | 9 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
468 | 10 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
469 | 11 | # PURPOSE. See the GNU General Public License for more details. | ||
470 | 12 | |||
471 | 13 | # You should have received a copy of the GNU General Public License along | ||
472 | 14 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
473 | 15 | |||
474 | 16 | """UTAH Client log parser.""" | ||
475 | 17 | |||
476 | 18 | |||
477 | 19 | import argparse | ||
478 | 20 | import jsonschema | ||
479 | 21 | import logging | ||
480 | 22 | import urllib2 | ||
481 | 23 | import yaml | ||
482 | 24 | |||
483 | 25 | |||
484 | 26 | class ParserError(Exception): | ||
485 | 27 | |||
486 | 28 | """Module level parse error exception.""" | ||
487 | 29 | |||
488 | 30 | pass | ||
489 | 31 | |||
490 | 32 | |||
491 | 33 | class UTAHParser(object): | ||
492 | 34 | |||
493 | 35 | """UTAH Client result parser class.""" | ||
494 | 36 | |||
495 | 37 | COMMAND_SCHEMA = { | ||
496 | 38 | 'type': 'object', | ||
497 | 39 | 'properties': { | ||
498 | 40 | 'cmd_type': {'type': 'string'}, | ||
499 | 41 | 'command': {'type': 'string'}, | ||
500 | 42 | 'returncode': {'type': 'integer'}, | ||
501 | 43 | 'start_time': {'type': 'string'}, | ||
502 | 44 | 'stderr': {'type': 'string'}, | ||
503 | 45 | 'stdout': {'type': 'string'}, | ||
504 | 46 | 'testcase': {'type': 'string'}, | ||
505 | 47 | 'testsuite': {'type': 'string'}, | ||
506 | 48 | 'time_delta': {'type': 'string'}, | ||
507 | 49 | 'user': {'type': 'string'}, | ||
508 | 50 | }, | ||
509 | 51 | } | ||
510 | 52 | |||
511 | 53 | CLIENT_OUTPUT_SCHEMA = { | ||
512 | 54 | 'type': 'object', | ||
513 | 55 | 'properties': { | ||
514 | 56 | 'runlist': { | ||
515 | 57 | 'type': 'string', | ||
516 | 58 | 'required': True, | ||
517 | 59 | }, | ||
518 | 60 | 'ran_at': { | ||
519 | 61 | 'type': 'string', | ||
520 | 62 | 'required': True, | ||
521 | 63 | }, | ||
522 | 64 | 'commands': { | ||
523 | 65 | 'type': 'array', | ||
524 | 66 | 'items': {'type': COMMAND_SCHEMA}, | ||
525 | 67 | 'required': True, | ||
526 | 68 | }, | ||
527 | 69 | 'fetch_errors': { | ||
528 | 70 | 'type': 'integer', | ||
529 | 71 | 'required': True, | ||
530 | 72 | }, | ||
531 | 73 | 'errors': { | ||
532 | 74 | 'type': 'integer', | ||
533 | 75 | 'required': True, | ||
534 | 76 | }, | ||
535 | 77 | 'failures': { | ||
536 | 78 | 'type': 'integer', | ||
537 | 79 | 'required': True, | ||
538 | 80 | }, | ||
539 | 81 | 'passes': { | ||
540 | 82 | 'type': 'integer', | ||
541 | 83 | 'required': True, | ||
542 | 84 | }, | ||
543 | 85 | 'uname': { | ||
544 | 86 | 'type': 'array', | ||
545 | 87 | 'items': {'type': 'string'}, | ||
546 | 88 | 'required': True, | ||
547 | 89 | }, | ||
548 | 90 | 'media-info': { | ||
549 | 91 | 'type': 'string', | ||
550 | 92 | 'required': True, | ||
551 | 93 | }, | ||
552 | 94 | 'install_type': { | ||
553 | 95 | 'type': 'string', | ||
554 | 96 | 'required': True, | ||
555 | 97 | }, | ||
556 | 98 | 'arch': { | ||
557 | 99 | 'type': 'string', | ||
558 | 100 | 'required': True, | ||
559 | 101 | }, | ||
560 | 102 | 'release': { | ||
561 | 103 | 'type': 'string', | ||
562 | 104 | 'required': True, | ||
563 | 105 | }, | ||
564 | 106 | 'build_number': { | ||
565 | 107 | 'type': 'string', | ||
566 | 108 | 'required': True, | ||
567 | 109 | }, | ||
568 | 110 | }, | ||
569 | 111 | } | ||
570 | 112 | |||
571 | 113 | def parse(self, logdata, as_string=False): | ||
572 | 114 | """Parse utah client results. | ||
573 | 115 | |||
574 | 116 | :param logdata: | ||
575 | 117 | should be either a filename or a handle to a file object. | ||
576 | 118 | |||
577 | 119 | :returns: the parsed yaml data or None. | ||
578 | 120 | |||
579 | 121 | :raises: ParserError on exceptions. | ||
580 | 122 | |||
581 | 123 | """ | ||
582 | 124 | |||
583 | 125 | if as_string: | ||
584 | 126 | return self._parse_string(logdata) | ||
585 | 127 | elif isinstance(logdata, str): | ||
586 | 128 | return self._parse_logfile(logdata) | ||
587 | 129 | else: | ||
588 | 130 | return self._parse_stream(logdata) | ||
589 | 131 | |||
590 | 132 | def _parse_stream(self, stream): | ||
591 | 133 | """Parse client output from stream. | ||
592 | 134 | |||
593 | 135 | :returns: validated decoded YAML data. | ||
594 | 136 | |||
595 | 137 | :raises: ParseError | ||
596 | 138 | |||
597 | 139 | """ | ||
598 | 140 | try: | ||
599 | 141 | data = yaml.load(stream) | ||
600 | 142 | except yaml.YAMLError as e: | ||
601 | 143 | logging.error(e) | ||
602 | 144 | raise ParserError(e.message) | ||
603 | 145 | |||
604 | 146 | # Allow empty yaml files | ||
605 | 147 | if data is None: | ||
606 | 148 | return data | ||
607 | 149 | |||
608 | 150 | try: | ||
609 | 151 | jsonschema.validate(data, self.CLIENT_OUTPUT_SCHEMA) | ||
610 | 152 | except jsonschema.ValidationError as e: | ||
611 | 153 | logging.error(e) | ||
612 | 154 | raise ParserError(e.message) | ||
613 | 155 | |||
614 | 156 | return data | ||
615 | 157 | |||
616 | 158 | def _parse_logfile(self, logfile): | ||
617 | 159 | """Parse client output log. | ||
618 | 160 | |||
619 | 161 | :returns: validated decoded YAML data. | ||
620 | 162 | |||
621 | 163 | :raises: ParseError | ||
622 | 164 | |||
623 | 165 | """ | ||
624 | 166 | data = None | ||
625 | 167 | |||
626 | 168 | if logfile.startswith('http'): | ||
627 | 169 | try: | ||
628 | 170 | fp = urllib2.urlopen(logfile) | ||
629 | 171 | except IOError as err: | ||
630 | 172 | raise ParserError( | ||
631 | 173 | 'IOError when downloading {}: {}' | ||
632 | 174 | .format(logfile, err)) | ||
633 | 175 | except urllib2.ContentTooShortError as err: | ||
634 | 176 | raise ParserError( | ||
635 | 177 | 'Error when downloading {} (probably interrupted): {}' | ||
636 | 178 | .format(logfile, err)) | ||
637 | 179 | data = self._parse_stream(fp) | ||
638 | 180 | else: | ||
639 | 181 | with open(logfile, 'r') as fp: | ||
640 | 182 | data = self._parse_stream(fp) | ||
641 | 183 | |||
642 | 184 | return data | ||
643 | 185 | |||
644 | 186 | def _parse_string(self, log_data): | ||
645 | 187 | """Parse client data from a string. | ||
646 | 188 | |||
647 | 189 | :returns: validated decoded YAML data. | ||
648 | 190 | |||
649 | 191 | :raises: ParseError | ||
650 | 192 | |||
651 | 193 | """ | ||
652 | 194 | try: | ||
653 | 195 | data = yaml.load(log_data) | ||
654 | 196 | |||
655 | 197 | # Allow empty yaml files | ||
656 | 198 | if data is None: | ||
657 | 199 | return data | ||
658 | 200 | except yaml.YAMLError as e: | ||
659 | 201 | logging.error(e) | ||
660 | 202 | raise ParserError(e.message) | ||
661 | 203 | |||
662 | 204 | try: | ||
663 | 205 | jsonschema.validate(data, self.CLIENT_OUTPUT_SCHEMA) | ||
664 | 206 | except jsonschema.ValidationError as e: | ||
665 | 207 | logging.error(e) | ||
666 | 208 | raise ParserError(e.message) | ||
667 | 209 | |||
668 | 210 | return data | ||
669 | 211 | |||
670 | 212 | |||
671 | 213 | if __name__ == "__main__": | ||
672 | 214 | arg_parser = argparse.ArgumentParser(description='phoenix bootstrapper') | ||
673 | 215 | arg_parser.add_argument('logfile', metavar='LOGFILE', type=str, | ||
674 | 216 | help='log file to parse') | ||
675 | 217 | |||
676 | 218 | args = arg_parser.parse_args() | ||
677 | 219 | |||
678 | 220 | parser = UTAHParser() | ||
679 | 221 | data = parser.parse(args.logfile) | ||
680 | 222 | print(data) |