diff options
| author | Andy Southgate <andy.southgate@citrix.com> | 2011-01-26 19:58:45 +0000 |
|---|---|---|
| committer | Andy Southgate <andy.southgate@citrix.com> | 2011-01-26 19:58:45 +0000 |
| commit | 0f868b00dbb2de469dde3519f2370e59937c4fc6 (patch) | |
| tree | 070491110c19cb447a2616145fedd2fbf21eac66 | |
| parent | cd346a2cda13833f976b9e838d67cf17c52f327e (diff) | |
OS-55: Added a test case for XenAPI file-based network injection
OS-55: Stubbed out utils.execute for all XenAPI VM tests, including command
simulation where necessary
| -rw-r--r-- | nova/tests/fake_utils.py | 98 | ||||
| -rw-r--r-- | nova/tests/test_xenapi.py | 83 |
2 files changed, 180 insertions, 1 deletions
diff --git a/nova/tests/fake_utils.py b/nova/tests/fake_utils.py new file mode 100644 index 000000000..f51d31e0c --- /dev/null +++ b/nova/tests/fake_utils.py @@ -0,0 +1,98 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Citrix Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""This modules stubs out functions in nova.utils +""" + +import re +import types + +from eventlet import greenthread + +from nova import exception +from nova import log as logging +from nova import utils + +LOG = logging.getLogger('nova.tests.fake_utils') + +_fake_execute_repliers = [] +_fake_execute_log = [] + + +def fake_execute_get_log(): + global _fake_execute_log + return _fake_execute_log + + +def fake_execute_clear_log(): + global _fake_execute_log + _fake_execute_log = [] + + +def fake_execute_set_repliers(repliers): + """Allows the client to configure replies to commands""" + global _fake_execute_repliers + _fake_execute_repliers = repliers + + +def fake_execute_default_reply_handler(*ignore_args): + """A reply handler for commands that haven't been added to the reply + list. Returns empty strings for stdout and stderr + """ + return '', '' + + +def fake_execute(cmd, process_input=None, addl_env=None, check_exit_code=True): + """This function stubs out execute, optionally executing + a preconfigued function to return expected data + """ + global _fake_execute_repliers + + LOG.debug(_("Faking execution of cmd (subprocess): %s"), cmd) + _fake_execute_log.append(cmd) + + reply_handler = fake_execute_default_reply_handler + + for fake_replier in _fake_execute_repliers: + if re.match(fake_replier[0], cmd): + reply_handler = fake_replier[1] + LOG.debug(_('Faked command matched %s') % fake_replier[0]) + break + + if isinstance(reply_handler, types.StringTypes): + # If the reply handler is a string, return it as stdout + reply = reply_handler, '' + else: + try: + # Alternative is a function, so call it + reply = reply_handler(cmd, process_input, addl_env, + check_exit_code) + except exception.ProcessExecutionError as e: + LOG.debug(_('Faked command raised an exception %s' % str(e))) + raise + + LOG.debug(_("Reply to faked command is stdout='%(0)s' stderr='%(1)s'") % + {'0': reply[0], '1': reply[1]}) + + # Replicate the sleep call in the real function + greenthread.sleep(0) + return reply + + +def stub_out_utils_execute(stubs): + fake_execute_set_repliers([]) + fake_execute_clear_log() + stubs.Set(utils, 'execute', fake_execute) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index f7f7102a8..8af47da63 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -18,11 +18,14 @@ Test suite for XenAPI """ +import os +import re import stubout from nova import db from nova import context from nova import flags +from nova import log as logging from nova import test from nova import utils from nova.auth import manager @@ -35,6 +38,9 @@ from nova.virt.xenapi.vmops import SimpleDH from nova.tests.db import fakes as db_fakes from nova.tests.xenapi import stubs from nova.tests.glance import stubs as glance_stubs +from nova.tests import fake_utils + +LOG = logging.getLogger('nova.tests.test_xenapi') FLAGS = flags.FLAGS @@ -166,6 +172,7 @@ class XenAPIVMTestCase(test.TestCase): stubs.stubout_stream_disk(self.stubs) glance_stubs.stubout_glance_client(self.stubs, glance_stubs.FakeGlance) + fake_utils.stub_out_utils_execute(self.stubs) self.conn = xenapi_conn.get_connection(False) def test_list_instances_0(self): @@ -306,11 +313,85 @@ class XenAPIVMTestCase(test.TestCase): FLAGS.xenapi_image_service = 'glance' self._test_spawn(1, 2, 3) - def test_spawn_netinject(self): + def test_spawn_netinject_file(self): + FLAGS.xenapi_image_service = 'glance' + db_fakes.stub_out_db_network_api(self.stubs, injected=True) + + self._tee_executed = False + + def _tee_handler(cmd, input, *ignore_args): + self.assertNotEqual(input, None) + + config = [line.strip() for line in input.split("\n")] + + # Find the start of eth0 configuration and check it + index = config.index('auto eth0') + self.assertEquals(config[index + 1:index + 8], [ + 'iface eth0 inet static', + 'address 10.0.0.3', + 'netmask 255.255.255.0', + 'broadcast 10.0.0.255', + 'gateway 10.0.0.1', + 'dns-nameservers 10.0.0.2', + '']) + + self._tee_executed = True + + return '', '' + + fake_utils.fake_execute_set_repliers([ + # Capture the sudo tee .../etc/network/interfaces command + (r'(sudo\s+)?tee.*interfaces', _tee_handler), + ]) + self._test_spawn(1, 2, 3, check_injection=True) + self.assertTrue(self._tee_executed) + + def test_spawn_netinject_xenstore(self): FLAGS.xenapi_image_service = 'glance' db_fakes.stub_out_db_network_api(self.stubs, injected=True) + + self._tee_executed = False + + def _mount_handler(cmd, *ignore_args): + # When mounting, create real files under the mountpoint to simulate + # files in the mounted filesystem + + # RegExp extracts the path of the mountpoint + match = re.match(r'(sudo\s+)?mount[^"]*"[^"]*"\s+"([^"]*)"', cmd) + self._tmpdir = match.group(2) + LOG.debug(_('Creating files in %s to simulate guest agent' % + self._tmpdir)) + os.makedirs(os.path.join(self._tmpdir, 'usr', 'sbin')) + # Touch the file using open + open(os.path.join(self._tmpdir, 'usr', 'sbin', + 'xe-update-networking'), 'w').close() + return '', '' + + def _umount_handler(cmd, *ignore_args): + # Umount would normall make files in the m,ounted filesystem + # disappear, so do that here + LOG.debug(_('Removing simulated guest agent files in %s' % + self._tmpdir)) + os.remove(os.path.join(self._tmpdir, 'usr', 'sbin', + 'xe-update-networking')) + os.rmdir(os.path.join(self._tmpdir, 'usr', 'sbin')) + os.rmdir(os.path.join(self._tmpdir, 'usr')) + return '', '' + + def _tee_handler(cmd, input, *ignore_args): + self._tee_executed = True + return '', '' + + fake_utils.fake_execute_set_repliers([ + (r'(sudo\s+)?mount', _mount_handler), + (r'(sudo\s+)?umount', _umount_handler), + (r'(sudo\s+)?tee.*interfaces', _tee_handler)]) self._test_spawn(1, 2, 3, check_injection=True) + # tee must not run in this case, where an injection-capable + # guest agent is detected + self.assertFalse(self._tee_executed) + def tearDown(self): super(XenAPIVMTestCase, self).tearDown() self.manager.delete_project(self.project) |
