Merge lp:~doanac/utah/server-cleanups into lp:utah

Proposed by Andy Doan
Status: Merged
Approved by: Javier Collado
Approved revision: 876
Merged at revision: 873
Proposed branch: lp:~doanac/utah/server-cleanups
Merge into: lp:utah
Diff against target: 873 lines (+427/-120) (has conflicts)
15 files modified
docs/source/reference.rst (+6/-0)
tests/__init__.py (+16/-0)
tests/common.py (+33/-0)
tests/test_debs.py (+89/-0)
tests/test_rsyslog.py (+6/-3)
tests/test_ssh.py (+33/-0)
tests/test_template.py (+50/-0)
utah/provisioning/baremetal/bamboofeeder.py (+0/-1)
utah/provisioning/baremetal/cobbler.py (+1/-4)
utah/provisioning/debs.py (+59/-0)
utah/provisioning/provisioning.py (+29/-50)
utah/provisioning/rsyslog.py (+29/-9)
utah/provisioning/ssh.py (+23/-18)
utah/provisioning/vm/vm.py (+0/-35)
utah/template.py (+53/-0)
Text conflict in utah/provisioning/provisioning.py
Text conflict in utah/provisioning/rsyslog.py
To merge this branch: bzr merge lp:~doanac/utah/server-cleanups
Reviewer Review Type Date Requested Status
Javier Collado (community) Approve
Review via email: mp+159705@code.launchpad.net

Description of the change

this shouldn't change any functionality. however, it cleans some things up so that a new feature I'm working on will be easier/cleaner to merge.

To post a comment you must log in.
Revision history for this message
Max Brustkern (nuclearbob) wrote :

I see two things that look like they'll work differently.

If you try to upload files and some of them don't exist, the exception will be raised before uploading any of them instead of after. That seems like it's probably fine.

Also, the handling of exceptions related to weird provisioning possibilities for Libvirt goes away. I think that makes the class slightly less robust for situations that we're not seeing at all right now. I wonder if we will see them in the future. Probably not. Seems reasonable, but I haven't tested anything yet. If you have, we're probably in good shape.

Revision history for this message
Andy Doan (doanac) wrote :

On 04/18/2013 03:50 PM, Max Brustkern wrote:
> I see two things that look like they'll work differently.
>
> If you try to upload files and some of them don't exist, the exception will be raised before uploading any of them instead of after. That seems like it's probably fine.

yeah, the results from the run don't change, so it seemed harmless.

> Also, the handling of exceptions related to weird provisioning possibilities for Libvirt goes away. I think that makes the class slightly less robust for situations that we're not seeing at all right now. I wonder if we will see them in the future. Probably not. Seems reasonable, but I haven't tested anything yet. If you have, we're probably in good shape.

the main difference was something I thought didn't make sense. In the
original code, you could specify !new, and if _load failed, we'd call
_create and try and correct your mistake for you. In the new code, that
would fail. alternatively it did a similar logic for new=True. I found
the logic confusing, not sure if it has practical uses?

Revision history for this message
Max Brustkern (nuclearbob) wrote :

I think it served some purposes when I was testing weird gymnastics of the original implementation regarding non-automatically-generated names, but I don't think it's really doing anything useful for us now.

Revision history for this message
Javier Collado (javier.collado) wrote :

The changes look good and I have been able to run the pass runlist
successfully. Please find below a few comments:

- tests/test_template.py
I think `_tmpdir` should be set in a `setupClass` method rather than in defined
as a global variable.

- utah/template.py
  - _add_backslash_filter
    `:rtype:` should be `string` and `:returns:` a description of what is
    returned.

  - as_buff
    The backslash filter could be added to `as_buff` at import time like this:
    def as_buff(template, **kw):
        <code>

    as_buff._env = ...

    This way `as_buff` would be easier to read and the code would take care
    only of the rendering, not the initialization.

flake8 output:
$ find . -name '*.py' | xargs flake8
./tests/test_debs.py:18:1: F401 'apt' imported but unused

pep257 output:
$ find . -name '*.py' | xargs pep257
test_debs.py:38:4: PEP257 Exported definitions should have docstrings.
test_debs.py:56:4: PEP257 Exported definitions should have docstrings.

lp:~doanac/utah/server-cleanups updated
872. By Andy Doan

improve test_template logic as per javier's review

873. By Andy Doan

template.py initialize improvement as per javier

874. By Andy Doan

clean up to test_debs.py as per javier

Revision history for this message
Andy Doan (doanac) wrote :

I think I've addressed Javier's issues now.

Revision history for this message
Javier Collado (javier.collado) wrote :

Thanks for the update. A few more comments:

- Test cases
The test cases are broken. It looks like the idea of initializing the template
environment at import time wasn't good for testing. The problem is the update
to `config.template_dir` in `tests/common.py` happens after
`template.as_buff._env` is already set.

I managed to make it work using something like this in `tests/common.py`:
from utah import template

...

def setUp():
    ....
    config.template_dir = ...
    reload(template)

However, I don't like much this trick, so I'll let you decide if you prefer
this or revert to the original code in which the environment was created when
the first template was rendered.

- Documentation
  - Please add ``utah.template`` to ``reference.rst``, so that it's included in
    the documentation.

  - Note that backslash characters must be escaped in the
    `_add_backslash_filter` documentation string to be properly rendered.
    Anyway, this not really an issue since private methods aren present in the
    documentation by default.

lp:~doanac/utah/server-cleanups updated
875. By Andy Doan

add new template module

876. By Andy Doan

fix broken test case

The test cases are broken. It looks like the idea of initializing the template
environment at import time wasn't good for testing. The problem is the update
to `config.template_dir` in `tests/common.py` happens after
`template.as_buff._env` is already set.

Revision history for this message
Andy Doan (doanac) wrote :

> However, I don't like much this trick, so I'll let you decide if you prefer
> this or revert to the original code in which the environment was created when
> the first template was rendered.

yeah - i think my first way is probably cleaner. last two revno's 875 and 876 should fix.

Revision history for this message
Javier Collado (javier.collado) wrote :

Thanks for all the updates. I think the changes are ready to be merged.

There's a small warning when building the documentation, but I'll fix it now
before merging:

server-cleanups/docs/source/reference.rst:60: WARNING: Title underline too short.

``utah.template``
----------------

review: Approve
Revision history for this message
Javier Collado (javier.collado) wrote :

I needed to remove 'tests/__init__.py' since it was causing packaging problems.

If it's needed to have that file, we can address the issue in a separate merge
request.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'docs/source/reference.rst'
2--- docs/source/reference.rst 2013-03-29 17:00:50 +0000
3+++ docs/source/reference.rst 2013-04-23 17:57:25 +0000
4@@ -56,6 +56,12 @@
5 .. automodule:: utah.timeout
6 :members:
7
8+``utah.template``
9+----------------
10+
11+.. automodule:: utah.template
12+ :members:
13+
14 ``utah.url``
15 ----------------
16
17
18=== added file 'tests/__init__.py'
19--- tests/__init__.py 1970-01-01 00:00:00 +0000
20+++ tests/__init__.py 2013-04-23 17:57:25 +0000
21@@ -0,0 +1,16 @@
22+# Ubuntu Testing Automation Harness
23+# Copyright 2013 Canonical Ltd.
24+
25+# This program is free software: you can redistribute it and/or modify it
26+# under the terms of the GNU General Public License version 3, as published
27+# by the Free Software Foundation.
28+
29+# This program is distributed in the hope that it will be useful, but
30+# WITHOUT ANY WARRANTY; without even the implied warranties of
31+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
32+# PURPOSE. See the GNU General Public License for more details.
33+
34+# You should have received a copy of the GNU General Public License along
35+# with this program. If not, see <http://www.gnu.org/licenses/>.
36+
37+"""tests for UTAH server code"""
38
39=== added file 'tests/common.py'
40--- tests/common.py 1970-01-01 00:00:00 +0000
41+++ tests/common.py 2013-04-23 17:57:25 +0000
42@@ -0,0 +1,33 @@
43+# Ubuntu Testing Automation Harness
44+# Copyright 2013 Canonical Ltd.
45+
46+# This program is free software: you can redistribute it and/or modify it
47+# under the terms of the GNU General Public License version 3, as published
48+# by the Free Software Foundation.
49+
50+# This program is distributed in the hope that it will be useful, but
51+# WITHOUT ANY WARRANTY; without even the implied warranties of
52+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
53+# PURPOSE. See the GNU General Public License for more details.
54+
55+# You should have received a copy of the GNU General Public License along
56+# with this program. If not, see <http://www.gnu.org/licenses/>.
57+
58+"""Provide common functions and content for self tests."""
59+
60+import shutil
61+import tempfile
62+
63+from utah import config
64+
65+
66+def setUp():
67+ """setup function called for the duration of the test run."""
68+
69+ # we need a consitent template across all the runs
70+ config.template_dir = tempfile.mkdtemp(prefix='utah-templates')
71+
72+
73+def tearDown():
74+ """Clean up after ourselves."""
75+ shutil.rmtree(config.template_dir)
76
77=== added file 'tests/test_debs.py'
78--- tests/test_debs.py 1970-01-01 00:00:00 +0000
79+++ tests/test_debs.py 2013-04-23 17:57:25 +0000
80@@ -0,0 +1,89 @@
81+# Ubuntu Testing Automation Harness
82+# Copyright 2013 Canonical Ltd.
83+
84+# This program is free software: you can redistribute it and/or modify it
85+# under the terms of the GNU General Public License version 3, as published
86+# by the Free Software Foundation.
87+
88+# This program is distributed in the hope that it will be useful, but
89+# WITHOUT ANY WARRANTY; without even the implied warranties of
90+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
91+# PURPOSE. See the GNU General Public License for more details.
92+
93+# You should have received a copy of the GNU General Public License along
94+# with this program. If not, see <http://www.gnu.org/licenses/>.
95+
96+"""Unit tests for the utah.provisioning.debs module."""
97+
98+import os
99+import shutil
100+import tempfile
101+import unittest
102+
103+from mock import patch
104+
105+from utah import config
106+from utah.provisioning.exceptions import UTAHProvisioningException
107+from utah.provisioning.debs import (
108+ _schema_deb,
109+ get_client_debs,
110+)
111+
112+
113+class TestDebs(unittest.TestCase):
114+
115+ """Tests the functions of the utah.provisioning.debs module."""
116+
117+ def setUp(self):
118+ """Set up a temp directory for test data."""
119+ self.pkgdir = config.packagedir
120+ self.tmpdir = tempfile.mkdtemp(prefix='utah_debs')
121+ config.packagedir = self.tmpdir
122+
123+ ver = 'FOO'
124+
125+ class dummy(object):
126+ def __init__(self):
127+ self.installedVersion = ver
128+ self.cache = {'utah': dummy()}
129+
130+ for pkg in ['common', 'client']:
131+ fname = 'utah-{}_{}_all.deb'.format(pkg, ver)
132+ pkg = os.path.join(self.tmpdir, fname)
133+ with open(pkg, 'w') as f:
134+ f.write('')
135+
136+ def tearDown(self):
137+ """Clean up stuff from the setup function."""
138+ config.packagedir = self.pkgdir
139+ shutil.rmtree(self.tmpdir)
140+
141+ def _setup_schema(self, oldest, latest):
142+ pkgfmt = 'python-jsonschema_0.{}-0~ppa1_all.deb'
143+
144+ for x in xrange(oldest, latest + 1):
145+ fname = pkgfmt.format(x)
146+ with open(os.path.join(self.tmpdir, fname), 'w') as f:
147+ f.write('')
148+ return pkgfmt.format(latest)
149+
150+ def test_missing_schema(self):
151+ """Ensure exception is raised when missing schema deb."""
152+ self.assertRaises(UTAHProvisioningException, _schema_deb)
153+
154+ def test_schema_deb(self):
155+ """Check that we return the latest schema deb."""
156+
157+ latest = self._setup_schema(1, 3)
158+
159+ #ensure we got the latest version of the schema
160+ deb = _schema_deb()
161+ self.assertEqual(latest, os.path.basename(deb))
162+
163+ @patch('apt.cache.Cache')
164+ def test_client_debs(self, cache):
165+ """Ensure get_client_debs API works."""
166+
167+ cache.side_effect = [self.cache, self.cache]
168+ self._setup_schema(2, 4)
169+ self.assertEqual(len(get_client_debs()), 3)
170
171=== modified file 'tests/test_rsyslog.py'
172--- tests/test_rsyslog.py 2013-04-22 11:14:38 +0000
173+++ tests/test_rsyslog.py 2013-04-23 17:57:25 +0000
174@@ -131,8 +131,9 @@
175 r = RSyslog("utah-test", '/tmp')
176 threading.Thread(target=self.producer, args=(r.port, messages)).start()
177
178- def booted_cb():
179+ def booted_cb(match):
180 self.test_future_booted = True
181+ self.assertEqual(match.string.rstrip(), steps[1]['message'])
182
183 self.test_future_booted = False
184 r.wait_for_install(steps, booted_cb)
185@@ -169,11 +170,13 @@
186 r = RSyslog("utah-test", '/tmp')
187 threading.Thread(target=self.producer, args=(r.port, messages)).start()
188
189- def booted_cb():
190+ def booted_cb(match):
191 self.test_callbacks_booted = True
192+ self.assertEqual(match.string.rstrip(), messages[1])
193
194- def blah_cb():
195+ def blah_cb(match):
196 self.test_callbacks_blah = True
197+ self.assertEqual(match.string.rstrip(), messages[-1])
198
199 self.test_callbacks_booted = self.test_callbacks_blah = False
200 callbacks = {
201
202=== added file 'tests/test_ssh.py'
203--- tests/test_ssh.py 1970-01-01 00:00:00 +0000
204+++ tests/test_ssh.py 2013-04-23 17:57:25 +0000
205@@ -0,0 +1,33 @@
206+# Ubuntu Testing Automation Harness
207+# Copyright 2013 Canonical Ltd.
208+
209+# This program is free software: you can redistribute it and/or modify it
210+# under the terms of the GNU General Public License version 3, as published
211+# by the Free Software Foundation.
212+
213+# This program is distributed in the hope that it will be useful, but
214+# WITHOUT ANY WARRANTY; without even the implied warranties of
215+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
216+# PURPOSE. See the GNU General Public License for more details.
217+
218+# You should have received a copy of the GNU General Public License along
219+# with this program. If not, see <http://www.gnu.org/licenses/>.
220+
221+"""Simple tests for the SSHMixin Object."""
222+
223+import unittest
224+
225+from utah.provisioning.exceptions import UTAHProvisioningException
226+from utah.provisioning.ssh import SSHMixin
227+
228+
229+class TestSSHMixin(unittest.TestCase):
230+
231+ """Performs testing of our SSHMixin class."""
232+
233+ def test_missing_files(self):
234+ """Make sure we exit *before* trying to provision if missing files."""
235+ ssh = SSHMixin()
236+ fname = '/this does not exist'
237+ with self.assertRaisesRegexp(UTAHProvisioningException, fname):
238+ ssh.uploadfiles(fname, '/tmp/')
239
240=== added file 'tests/test_template.py'
241--- tests/test_template.py 1970-01-01 00:00:00 +0000
242+++ tests/test_template.py 2013-04-23 17:57:25 +0000
243@@ -0,0 +1,50 @@
244+# Ubuntu Testing Automation Harness
245+# Copyright 2013 Canonical Ltd.
246+
247+# This program is free software: you can redistribute it and/or modify it
248+# under the terms of the GNU General Public License version 3, as published
249+# by the Free Software Foundation.
250+
251+# This program is distributed in the hope that it will be useful, but
252+# WITHOUT ANY WARRANTY; without even the implied warranties of
253+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
254+# PURPOSE. See the GNU General Public License for more details.
255+
256+# You should have received a copy of the GNU General Public License along
257+# with this program. If not, see <http://www.gnu.org/licenses/>.
258+
259+"""Module to test our template utility."""
260+
261+import os
262+import unittest
263+
264+from utah import config
265+from utah import template
266+
267+from tests.common import ( # NOQA
268+ setUp, # Used by nosetests
269+ tearDown, # Used by nosetests
270+)
271+
272+
273+class TestTemplate(unittest.TestCase):
274+
275+ """Unit test class for our template utility."""
276+
277+ def setUp(self):
278+ """Executed before each test_ method is called."""
279+ with open(os.path.join(config.template_dir, 'tmp.jinja2'), 'w') as f:
280+ f.write('line1\nfoo={{foo}}')
281+
282+ def test_as_buff(self):
283+ """minimal test to make sure the template api works."""
284+ buff = template.as_buff('tmp.jinja2', foo='bar')
285+ self.assertEqual(buff, 'line1\nfoo=bar')
286+
287+ def test_write(self):
288+ """minimal test to make sure the file write api works."""
289+ self.setUp()
290+ f = os.path.join(config.template_dir, 'tmp.txt')
291+ template.write('tmp.jinja2', f, foo='bar')
292+ with open(f, 'r') as f:
293+ self.assertEqual(f.read(), 'line1\nfoo=bar')
294
295=== modified file 'utah/provisioning/baremetal/bamboofeeder.py'
296--- utah/provisioning/baremetal/bamboofeeder.py 2013-04-04 18:11:06 +0000
297+++ utah/provisioning/baremetal/bamboofeeder.py 2013-04-23 17:57:25 +0000
298@@ -284,7 +284,6 @@
299 retry(self.sshcheck, logmethod=self.logger.info,
300 retry_timeout=config.checktimeout)
301
302- self.provisioned = True
303 self.active = True
304 self._uuid_check()
305 self.logger.info('System installed')
306
307=== modified file 'utah/provisioning/baremetal/cobbler.py'
308--- utah/provisioning/baremetal/cobbler.py 2013-04-10 15:26:26 +0000
309+++ utah/provisioning/baremetal/cobbler.py 2013-04-23 17:57:25 +0000
310@@ -84,8 +84,6 @@
311 if self.name not in machines:
312 raise UTAHBMProvisioningException(
313 'No machine named {} exists in cobbler'.format(self.name))
314- else:
315- self.provisioned = True
316
317 def _create(self):
318 """Install the OS on the machine."""
319@@ -218,12 +216,11 @@
320 if self.installtype == 'desktop':
321 self._removenfs()
322
323- self.provisioned = True
324 self.active = True
325 self._uuid_check()
326 self.logger.info('System installed')
327
328- def _disable_netboot(self):
329+ def _disable_netboot(self, match):
330 self.logger.info('install seems to have booted, disabling netboot')
331 self._cobble(['system', 'edit', '--name={}'.format(self.name),
332 '--netboot-enabled=N'])
333
334=== added file 'utah/provisioning/debs.py'
335--- utah/provisioning/debs.py 1970-01-01 00:00:00 +0000
336+++ utah/provisioning/debs.py 2013-04-23 17:57:25 +0000
337@@ -0,0 +1,59 @@
338+# Ubuntu Testing Automation Harness
339+# Copyright 2012 Canonical Ltd.
340+
341+# This program is free software: you can redistribute it and/or modify it
342+# under the terms of the GNU General Public License version 3, as published
343+# by the Free Software Foundation.
344+
345+# This program is distributed in the hope that it will be useful, but
346+# WITHOUT ANY WARRANTY; without even the implied warranties of
347+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
348+# PURPOSE. See the GNU General Public License for more details.
349+
350+# You should have received a copy of the GNU General Public License along
351+# with this program. If not, see <http://www.gnu.org/licenses/>.
352+
353+"""Module provides paths to the utah client debs needed for install."""
354+
355+import apt.cache
356+import os
357+
358+from glob import glob
359+
360+from utah import config
361+from utah.provisioning.exceptions import UTAHProvisioningException
362+
363+
364+def _utah_deb(deb):
365+ utah_version = apt.cache.Cache()['utah'].installedVersion
366+ if utah_version is None:
367+ raise UTAHProvisioningException("UTAH package isn't installed")
368+
369+ basename = ('utah-{}_{}_all.deb'.format(deb, utah_version))
370+ path = os.path.join(config.packagedir, basename)
371+ if not os.path.isfile(path):
372+ raise UTAHProvisioningException(
373+ 'UTAH {} binary package file not found in {}'.format(path, deb))
374+ return path
375+
376+
377+def _schema_deb():
378+ debpath = config.packagedir
379+ deb_file_glob = os.path.join(debpath, 'python-jsonschema_*_all.deb')
380+ deb_files = glob(deb_file_glob)
381+ if not deb_files:
382+ msg = 'python-jsonschema package file not found in {}'.format(debpath)
383+ raise UTAHProvisioningException(msg)
384+ deb_files.sort()
385+ return deb_files[-1]
386+
387+
388+def get_client_debs():
389+ """Return paths to all .debs required to install the utah-client.
390+
391+ :returns: Local paths to deb files
392+ :rtype: list
393+ :raises UTAHProvisioningException: When package(s) can't be found
394+
395+ """
396+ return [_schema_deb(), _utah_deb('common'), _utah_deb('client')]
397
398=== modified file 'utah/provisioning/provisioning.py'
399--- utah/provisioning/provisioning.py 2013-04-19 11:42:45 +0000
400+++ utah/provisioning/provisioning.py 2013-04-23 17:57:25 +0000
401@@ -38,10 +38,12 @@
402 import utah.timeout
403
404 from utah import config
405+from utah import template
406 from utah.cleanup import cleanup
407 from utah.process import ProcessRunner
408 from utah.iso import ISO
409 from utah.preseed import Preseed
410+from utah.provisioning.debs import get_client_debs
411 from utah.provisioning.rsyslog import RSyslog
412 from utah.provisioning.exceptions import UTAHProvisioningException
413 from utah.retry import retry
414@@ -206,17 +208,6 @@
415 path, getattr(self, item))
416
417 self.finalpreseed = self.preseed
418-
419- self.template_env = \
420- Environment(loader=FileSystemLoader(config.template_dir))
421-
422- def add_backslash_filter(value):
423- """Append backslash character to each line"""
424- # TODO: consider moving this elsewhere
425- return '\n'.join(['{}\\'.format(line)
426- for line in value.splitlines()])
427- self.template_env.filters['add_backslash'] = add_backslash_filter
428-
429 self.logger.debug('Machine init finished')
430
431 @property
432@@ -368,6 +359,7 @@
433 utah.timeout.timeout(timeout, retry, self.pingcheck,
434 logmethod=logmethod, retry_timeout=checktimeout)
435
436+<<<<<<< TREE
437 def getutahdeb(self, deb):
438 """Return path of utah deb files.
439
440@@ -417,6 +409,8 @@
441 deb_file = deb_files[0]
442 return deb_file
443
444+=======
445+>>>>>>> MERGE-SOURCE
446 def installclient(self):
447 """Install the required packages on the machine.
448
449@@ -427,9 +421,7 @@
450 """
451 self.logger.info('Installing client deb on machine')
452 tmppath = os.path.normpath('/tmp')
453- debs = [self.getjsonschemadeb(), self.getutahdeb('common'),
454- self.getutahdeb('client')]
455- for deb in debs:
456+ for deb in get_client_debs():
457 try:
458 self.uploadfiles([deb], tmppath)
459 except UTAHProvisioningException as err:
460@@ -446,11 +438,9 @@
461 except AttributeError:
462 raise err
463
464- remote_deb = os.path.join(tmppath, os.path.basename(deb))
465- template = self.template_env.get_template(
466- 'install-deb-command.jinja2')
467- install_command = template.render(deb=remote_deb)
468- returncode, _stdout, stderr = self.run(install_command, root=True)
469+ deb = os.path.join(tmppath, os.path.basename(deb))
470+ cmd = template.as_buff('install-deb-command.jinja2', deb=deb)
471+ returncode, _stdout, stderr = self.run(cmd, root=True)
472 if (returncode != 0 or
473 re.search(r'script returned error exit status \d+',
474 stderr)):
475@@ -468,20 +458,16 @@
476 provisioncheck() or activecheck() should be used.
477
478 """
479- # TODO: make sure provisioned is set consistently by
480- # provisioncheck, _provision, and _load
481- # i.e., right now CobblerMachine has a _load method that sets it
482- # VMs have a separate _provision that does set it and a _load that
483- # by necessity doesn't
484- # Do we really need this function separate from provisioncheck?
485- # Let's discuss this one
486 if not self.new:
487 try:
488 self.logger.debug('Trying to load existing machine')
489 self._load()
490+ self.provisioned = True
491+ return
492 except Exception as err:
493 self.logger.debug('Failed to load machine: %s', str(err))
494 self._create()
495+ self.provisioned = True
496
497 def _create(self):
498 # TODO: discuss separation of this vs. start when separating Install
499@@ -814,23 +800,19 @@
500 os.path.join(tmpdir, 'initrd.d', 'utah-ssh-key'))
501
502 self.logger.info('Creating latecommand scripts')
503- template = self.template_env.get_template('utah-latecommand.jinja2')
504- latecommand = template.render(user=config.user,
505- uuid=self.uuid,
506- log_file='/target/var/log/utah-install')
507 filename = os.path.join(tmpdir, 'initrd.d', 'utah-latecommand')
508- with open(filename, 'w') as f:
509- f.write(latecommand)
510+ template.write('utah-latecommand.jinja2',
511+ filename,
512+ user=config.user,
513+ uuid=self.uuid,
514+ log_file='/target/var/log/utah-install')
515
516- template = self.template_env.get_template(
517- 'utah-latecommand-in-target.jinja2')
518- latecommand = template.render(
519- packages=config.installpackages,
520- log_file='/var/log/utah-install')
521 filename = os.path.join(tmpdir, 'initrd.d',
522 'utah-latecommand-in-target')
523- with open(filename, 'w') as f:
524- f.write(latecommand)
525+ template.write('utah-latecommand-in-target.jinja2',
526+ filename,
527+ packages=config.installpackages,
528+ log_file='/var/log/utah-install')
529
530 def _setuppreseed(self, tmpdir=None):
531 """Rewrite the preseed to automate installation and access."""
532@@ -884,12 +866,12 @@
533 question.prepend('ubiquity ubiquity/summary note')
534 question.prepend('ubiquity ubiquity/reboot boolean true')
535
536- template = self.template_env.get_template('latecommand-wrapper.jinja2')
537 filename = os.path.join(tmpdir, 'initrd.d', 'latecommand-wrapper')
538 target_log_file = '/target{}'.format(log_file)
539- with open(filename, 'w') as f:
540- f.write(template.render(latecommand=question.value.text,
541- log_file=target_log_file))
542+ template.write('latecommand-wrapper.jinja2',
543+ filename,
544+ latecommand=question.value.text,
545+ log_file=target_log_file)
546 question.value = (
547 'sh latecommand-wrapper || '
548 'logger -s -t utah "Late command failure detected" '
549@@ -956,14 +938,12 @@
550 self.logger.info('Inserting preseed into casper')
551 if tmpdir is None:
552 tmpdir = self.tmpdir
553- template = self.template_env.get_template(
554- 'casper-preseed-script.jinja2')
555- preseedscript = template.render()
556+
557 casper_dir = os.path.join(tmpdir, 'initrd.d', 'scripts',
558 'casper-bottom')
559+
560 filename = os.path.join(casper_dir, 'utah')
561- with open(filename, 'w') as f:
562- f.write(preseedscript)
563+ template.write('casper-preseed-script.jinja2', filename)
564 os.chmod(filename, 0755)
565
566 orderfilename = os.path.join(casper_dir, 'ORDER')
567@@ -1009,7 +989,6 @@
568 "-f /var/log/syslog \n")
569
570 self.logger.info('Creating rsyslog config file')
571- template = self.template_env.get_template('50-utahdefault.conf.jinja2')
572 conffilename = os.path.join(tmpdir, 'initrd.d', '50-utahdefault.conf')
573 with open(conffilename, 'w') as f:
574 if self.rsyslog.port:
575@@ -1018,7 +997,7 @@
576 else:
577 self.logger.debug('setting up logging to go to serial console')
578 dest = '|/dev/ttyS0'
579- f.write(template.render(dest=dest))
580+ f.write(template.as_buff('50-utahdefault.conf.jinja2', dest=dest))
581
582 def _repackinitrd(self, tmpdir=None):
583 """Pack an initrd from our directory.
584
585=== modified file 'utah/provisioning/rsyslog.py'
586--- utah/provisioning/rsyslog.py 2013-04-22 10:59:26 +0000
587+++ utah/provisioning/rsyslog.py 2013-04-23 17:57:25 +0000
588@@ -132,7 +132,11 @@
589 # i.e. LP#1100386
590 try:
591 self._wait_for_steps(steps, logfile, callbacks)
592+<<<<<<< TREE
593 except UTAHTimeout:
594+=======
595+ except UTAHException:
596+>>>>>>> MERGE-SOURCE
597 self.logger.warning('Timed out waiting for boot, but continuing')
598
599 def _wait_for_steps(self, steps, logfile, callbacks):
600@@ -152,6 +156,7 @@
601 future_pats = self._future_patterns(steps, x)
602 pattern.extend(future_pats)
603 self.logger.info('Waiting %ds for: %s', timeout, message)
604+<<<<<<< TREE
605 match = self._wait_for(f, pattern, message, timeout)
606 if match is None:
607 remaining_messages = [step['message']
608@@ -166,24 +171,28 @@
609 self.logger.error(log_message)
610 raise UTAHTimeout(log_message)
611 if match in fail_pattern:
612+=======
613+ pattern, match = self._wait_for(f, pattern, message, timeout)
614+ if pattern in fail_pattern:
615+>>>>>>> MERGE-SOURCE
616 raise UTAHException('Failure pattern found: {}'
617- .format(match))
618- if match in future_pats:
619+ .format(pattern))
620+ if pattern in future_pats:
621 msg = 'Expected pattern missed, matched future pattern: %s'
622 self.logger.warn(msg, match)
623- x = self._fast_forward(steps, match, callbacks)
624+ x = self._fast_forward(steps, pattern, match, callbacks)
625 else:
626 self.logger.info('Matched pattern %r for %r message',
627- match, message)
628+ pattern, message)
629
630- self._do_callback(steps[x], callbacks)
631+ self._do_callback(steps[x], callbacks, match)
632 x += 1
633
634 @staticmethod
635- def _do_callback(step, callbacks):
636+ def _do_callback(step, callbacks, match):
637 for name, func in callbacks.iteritems():
638 if func and step.get(name, False):
639- func()
640+ func(match)
641
642 @staticmethod
643 def _future_patterns(steps, index):
644@@ -198,7 +207,7 @@
645 return patterns
646
647 @staticmethod
648- def _fast_forward(steps, pattern, callbacks):
649+ def _fast_forward(steps, pattern, match, callbacks):
650 """Figure out what should be the next step.
651
652 Look through each item in the steps array to find the index of the
653@@ -211,7 +220,7 @@
654 x = 0
655 while x < len(steps):
656 # make sure we don't skip a callback
657- RSyslog._do_callback(steps[x], callbacks)
658+ RSyslog._do_callback(steps[x], callbacks, match)
659
660 patterns = steps[x]['pattern']
661 if not isinstance(patterns, list):
662@@ -232,8 +241,19 @@
663 sys.stderr.write(data)
664 writer.write(data)
665 for pat in pats:
666+<<<<<<< TREE
667 if pat.match(data):
668 return pat.pattern
669+=======
670+ match = pat.match(data)
671+ if match:
672+ return (pat.pattern, match)
673+ log_message = ('Timeout ({}) occurred for {!r} message'
674+ .format(timeout, message))
675+ # Log error message to write the timestamp when the timeout happened
676+ self.logger.error(log_message)
677+ raise UTAHException(log_message)
678+>>>>>>> MERGE-SOURCE
679
680 def _read_udp(self):
681 data = None
682
683=== modified file 'utah/provisioning/ssh.py'
684--- utah/provisioning/ssh.py 2013-04-18 14:27:33 +0000
685+++ utah/provisioning/ssh.py 2013-04-23 17:57:25 +0000
686@@ -166,6 +166,18 @@
687
688 return retval, ''.join(stdout_lines), ''.join(stderr_lines)
689
690+ @staticmethod
691+ def _check_files(files):
692+ failed = []
693+ for f in files:
694+ if not os.path.isfile(f):
695+ failed.append(f)
696+ if len(failed):
697+ msg = 'Files do not exist: {}'.format(' '.join(failed))
698+ err = UTAHProvisioningException(msg)
699+ err.files = failed
700+ raise err
701+
702 def uploadfiles(self, files, target=os.path.normpath('/tmp/')):
703 """Copy a file or list of files to a target directory on the machine.
704
705@@ -178,8 +190,9 @@
706 if isinstance(files, basestring):
707 files = [files]
708
709+ self._check_files(files)
710+
711 self.activecheck()
712- failed = []
713 sftp_client = None
714 try:
715 self.ssh_client.connect(self.name,
716@@ -187,15 +200,12 @@
717 key_filename=config.sshprivatekey)
718 sftp_client = self.ssh_client.open_sftp()
719 for localpath in files:
720- if os.path.isfile(localpath):
721- self.ssh_logger.info('Uploading {} from the host '
722- 'to {} on the machine'
723- .format(localpath, target))
724- remotepath = os.path.join(target,
725- os.path.basename(localpath))
726- sftp_client.put(localpath, remotepath)
727- else:
728- failed.append(localpath)
729+ self.ssh_logger.info(
730+ 'Uploading %s from the host to %s on the machine',
731+ localpath,
732+ target)
733+ remotepath = os.path.join(target, os.path.basename(localpath))
734+ sftp_client.put(localpath, remotepath)
735 except paramiko.BadHostKeyException as err:
736 raise UTAHProvisioningException(
737 'Host key exception encountered; '
738@@ -207,11 +217,6 @@
739 finally:
740 if sftp_client:
741 sftp_client.close()
742- if len(failed) > 0:
743- err = UTAHProvisioningException('Files do not exist: {}'
744- .format(' '.join(failed)))
745- err.files = failed
746- raise err
747
748 def downloadfiles(self, files, target=os.path.normpath('/tmp/')):
749 """Copy a file or list of files from the machine to a local target.
750@@ -382,6 +387,9 @@
751 # No cleanup needed for systems that are already provisioned
752 self.clean = False
753
754+ self.active = False
755+ self.provisioned = True
756+
757 # System is expected to be available already, so there's no need to
758 # wait before trying to connect through ssh
759 self.check_timeout = 3
760@@ -391,9 +399,6 @@
761 if installtype is None:
762 self.installtype = config.installtype
763
764- self.template_env = \
765- Environment(loader=FileSystemLoader(config.template_dir))
766-
767 def activecheck(self):
768 """Check if machine is active.
769
770
771=== modified file 'utah/provisioning/vm/vm.py'
772--- utah/provisioning/vm/vm.py 2013-04-04 17:17:21 +0000
773+++ utah/provisioning/vm/vm.py 2013-04-23 17:57:25 +0000
774@@ -71,41 +71,6 @@
775 self.vm = self.lv.lookupByName(self.name)
776 self.logger.info('VM loaded')
777
778- def _provision(self):
779- """Make an existing VM available using libvirt to look up the VM."""
780- self.logger.info('Provisioning VM')
781- if self.new:
782- self.logger.debug('New VM requested')
783- try:
784- self._load()
785- self.logger.error('VM already exists')
786- raise UTAHVMProvisioningException(
787- 'New VM requested, but {} already exists'
788- .format(self.name))
789- except libvirt.libvirtError as err:
790- if err.get_error_code() == 42:
791- self._create()
792- else:
793- raise err
794-
795- try:
796- self._load()
797- except libvirt.libvirtError as err:
798- if err.get_error_code() == 42:
799- self.logger.debug('Lookup failed')
800- try:
801- self._create()
802- self._load()
803- except UTAHVMProvisioningException as error:
804- self.logger.error('VM lookup failed')
805- raise UTAHVMProvisioningException(
806- 'Cannot find VM named {}: {}'
807- .format(self.name, str(error)))
808- else:
809- raise err
810- self.provisioned = True
811- self.logger.info('VM provisioned')
812-
813 def activecheck(self):
814 """Verify the machine is provisioned, then start it if needed. """
815 self.logger.debug('Checking if VM is active')
816
817=== added file 'utah/template.py'
818--- utah/template.py 1970-01-01 00:00:00 +0000
819+++ utah/template.py 2013-04-23 17:57:25 +0000
820@@ -0,0 +1,53 @@
821+# Ubuntu Testing Automation Harness
822+# Copyright 2013 Canonical Ltd.
823+
824+# This program is free software: you can redistribute it and/or modify it
825+# under the terms of the GNU General Public License version 3, as published
826+# by the Free Software Foundation.
827+
828+# This program is distributed in the hope that it will be useful, but
829+# WITHOUT ANY WARRANTY; without even the implied warranties of
830+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
831+# PURPOSE. See the GNU General Public License for more details.
832+
833+# You should have received a copy of the GNU General Public License along
834+# with this program. If not, see <http://www.gnu.org/licenses/>.
835+
836+"""Provide easy accessors to the Jinja2 template library"""
837+
838+import os
839+
840+from jinja2 import Environment, FileSystemLoader
841+
842+from utah import config
843+
844+
845+def _add_backslash_filter(value):
846+ r"""Append backslash character to each line.
847+
848+ :returns: an version of the string where \\'s have been to each \\n
849+ :rtype: string
850+
851+ """
852+ return '\n'.join(['{}\\'.format(line) for line in value.splitlines()])
853+
854+
855+def as_buff(template, **kw):
856+ """Return the rendered template as a string."""
857+ if not getattr(as_buff, '_env', None):
858+ paths = [
859+ config.template_dir,
860+ os.path.join(os.path.dirname(__file__), '../templates'),
861+ ]
862+ as_buff._env = \
863+ Environment(loader=FileSystemLoader(paths))
864+ as_buff._env.filters['add_backslash'] = _add_backslash_filter
865+ template = as_buff._env.get_template(template)
866+ return template.render(kw)
867+
868+
869+def write(template, path, **kw):
870+ """Render the given template as a file."""
871+ content = as_buff(template, **kw)
872+ with open(path, 'w') as f:
873+ f.write(content)

Subscribers

People subscribed via source and target branches

to all changes: