From 712fa9c92c2d35b0b7993b5a30c25fdad70fcae3 Mon Sep 17 00:00:00 2001 From: Leo Simons Date: Thu, 7 Aug 2014 16:36:16 +0200 Subject: [PATCH] Tests for update_config.py These are failing on my machine with cloud.log lines like 2014-08-07 14:34:09,509 Add dev eth2 table Table_eth2 10.0.2.0/24 2014-08-07 14:34:09,511 Address 10.0.2.106/24 on device eth2 not configured 2014-08-07 14:34:10,513 Device eth2 cannot be configured - device was not found I think it's correct that they are failing -- this is work in progress. --- test/systemvm/__init__.py | 36 +++++++- test/systemvm/test_hello_systemvm.py | 27 +++--- test/systemvm/test_update_config.py | 120 +++++++++++++++++++++++++++ tools/vagrant/systemvm/Vagrantfile | 5 +- 4 files changed, 172 insertions(+), 16 deletions(-) create mode 100644 test/systemvm/test_update_config.py diff --git a/test/systemvm/__init__.py b/test/systemvm/__init__.py index 429b3d3e963..1b8aa1c6b46 100644 --- a/test/systemvm/__init__.py +++ b/test/systemvm/__init__.py @@ -43,6 +43,8 @@ except (NameError, ImportError): return output subprocess.check_output = check_output +import logging +logging.getLogger('paramiko.transport').setLevel(logging.WARNING) from vagrant import Vagrant from unittest import TestCase @@ -57,6 +59,7 @@ from StringIO import StringIO from nose.plugins.attrib import attr import os.path +import sys _defaultVagrantDir = os.path.abspath(os.path.join( @@ -180,7 +183,36 @@ class SystemVMTestCase(TestCase): # env.host_string = self._env_host_string_orig -def has_line(location, line): +def has_line(location, line, ctx=3): with hide("everything"): text = run('cat "%s"' % location) - return text.find(line) >= 0 + text_len = len(text) + pos = text.find(line) + if pos < 0: + return False, '' + start = end = pos + newlines = 0 + while start > 0: + if text[start] == '\n': + newlines += 1 + if newlines > ctx: + break + start -= 1 + newlines = 0 + while end < text_len: + if text[end] == '\n': + newlines += 1 + if newlines > ctx: + break + end += 1 + context = '...\n' + text[start:end].strip() + '\n...' + return True, context + + +def print_doc(name, data, target=None): + if target is None: + target = sys.stdout + print >>target, " ", "-" * 4, name, "-" * max(68-4-2-len(name), 0) + for line in data.split('\n'): + print >>target, " ", line + print >>target, " ", "-" * 68 diff --git a/test/systemvm/test_hello_systemvm.py b/test/systemvm/test_hello_systemvm.py index ee231b27608..f0b3260be0e 100644 --- a/test/systemvm/test_hello_systemvm.py +++ b/test/systemvm/test_hello_systemvm.py @@ -17,35 +17,38 @@ """Example of using paramiko and envassert for systemvm tests.""" -from nose.plugins.attrib import attr +# from nose.plugins.attrib import attr from envassert import file, package, user from cuisine import file_write try: - from . import SystemVMTestCase, has_line + from . import SystemVMTestCase, has_line, print_doc except (ImportError, ValueError): - from systemvm import SystemVMTestCase, has_line + from systemvm import SystemVMTestCase, has_line, print_doc class HelloSystemVMTestCase(SystemVMTestCase): - @attr(tags=["systemvm"], required_hardware="true") - def test_hello_systemvm_paramiko(self): + # @attr(tags=["systemvm"], required_hardware="true") + def disabled_hello_systemvm_paramiko(self): """Test we can connect to the systemvm over ssh, low-level with paramiko""" stdin, stdout, stderr = self.sshClient.exec_command('echo hello') result = stdout.read().strip() self.assertEqual('hello', result) - @attr(tags=["systemvm"], required_hardware="true") - def test_hello_systemvm_envassert(self): + # @attr(tags=["systemvm"], required_hardware="true") + def disabled_test_hello_systemvm_envassert(self): """Test we can run envassert assertions on the systemvm""" assert file.exists('/etc/hosts') for packageName in ['dnsmasq', 'haproxy', 'keepalived', 'curl']: - assert package.installed(packageName) + assert package.installed(packageName), 'package %s should be installed' % packageName - assert user.exists('cloud') + assert user.exists('cloud'), 'user cloud should exist' - @attr(tags=["systemvm"], required_hardware="true") - def test_hello_systemvm_cuisine(self): + # @attr(tags=["systemvm"], required_hardware="true") + def disabled_hello_systemvm_cuisine(self): """Test we can run cuisine on the systemvm""" file_write('/tmp/run_cuisine', '\n\nsuccess!\n') - assert has_line('/tmp/run_cuisine', 'success!') + found, context = has_line('/tmp/run_cuisine', 'success!') + if not found: + print_doc('/tmp/cuisine', context) + assert found, '/tmp/run_cuisine should contain "success!"' diff --git a/test/systemvm/test_update_config.py b/test/systemvm/test_update_config.py new file mode 100644 index 00000000000..f847c05379b --- /dev/null +++ b/test/systemvm/test_update_config.py @@ -0,0 +1,120 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +"""Basic integration test that runs update_config.py.""" + +from nose.plugins.attrib import attr +from cuisine import file_write, run +from fabric.api import hide +import json +import random +import datetime + +try: + from . import SystemVMTestCase, has_line, print_doc +except (ImportError, ValueError): + from systemvm import SystemVMTestCase, has_line, print_doc + + +def deep_copy(obj): + return json.loads(json.dumps(obj)) + + +class UpdateConfigTestCase(SystemVMTestCase): + basic_config = { + "ip_address": [ + { + "public_ip": "10.0.2.102", + "source_nat": True, + "add": True, + "one_to_one_nat": False, + "first_i_p": False, + "gateway": "10.0.2.1", + "netmask": "255.255.255.0", + "vif_mac_address": "06:cb:aa:00:00:03", + "nic_dev_id": 1, + "new_nic": False + } + ], + "type": "ips" + } + + def update_config(self, config): + config_json = json.dumps(config, indent=2) + print_doc('config.json', config_json) + file_write('/etc/cloudstack/update_config_test.json', config_json) + with hide("everything"): + result = run("python /opt/cloud/bin/update_config.py update_config_test.json", + timeout=600, warn_only=True) + print result + assert result.succeeded, 'update_config.py ran without errors' + assert result.find("Convergence is achieved") >= 0, 'update_config.py should report convergence' + + def clear_log(self): + tstamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + run("test -f /var/log/cloud.log && mv /var/log/cloud.log /var/log/cloud.log.%s || true" % tstamp) + + def setUp(self): + super(UpdateConfigTestCase, self).setUp() + self.clear_log() + + def check_no_errors(self): + # todo config update should exit 1 on convergence errors! + found, context = has_line('/var/log/cloud.log', 'cannot be configured') + if found: + print_doc('/var/log/cloud.log', context) + assert not found, 'cloud.log should not contain "cannot be configured"' + + @attr(tags=["systemvm"], required_hardware="true") + def test_basic_config(self): + self.update_config(self.basic_config) + self.check_no_errors() + # should be able to run twice with same config + self.clear_log() + self.update_config(self.basic_config) + self.check_no_errors() + + @attr(tags=["systemvm"], required_hardware="true") + def test_various_random_ip_addresses(self): + r = random.Random() + r.seed() + for i in range(0, 10): + # todo need to know what kind of configurations are valid! + config = deep_copy(self.basic_config) + ip_address = deep_copy(self.basic_config["ip_address"][0]) + ip_address["public_ip"] = "10.0.2.%d" % (i + 103,) + ip_address["source_nat"] = r.choice((True, False)) + ip_address["add"] = r.choice((True, False)) + ip_address["one_to_one_nat"] = r.choice((True, False)) + ip_address["first_i_p"] = r.choice((True, False)) + ip_address["nic_dev_id"] = r.choice((0, 1, 2)) + if ip_address["nic_dev_id"] > 0: + ip_address["new_nic"] = True + else: + ip_address["new_nic"] = False + config["ip_address"].append(ip_address) + # runs a bunch of times adding an IP address each time + self.update_config(config) + self.check_no_errors() + self.clear_log() + # run again with just the basic config; this should remove the IP addresses? + self.update_config(self.basic_config) + + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/tools/vagrant/systemvm/Vagrantfile b/tools/vagrant/systemvm/Vagrantfile index 03d9ad420e9..f44ac914f18 100644 --- a/tools/vagrant/systemvm/Vagrantfile +++ b/tools/vagrant/systemvm/Vagrantfile @@ -78,8 +78,9 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 'config/opt' => '/opt', 'config/root' => '/root', 'config/var' => '/var', - 'vpn/etc' => '/etc', - 'vpn/opt' => '/opt', + # cannot have two rsyncs pointing to the same dir + # 'vpn/etc' => '/etc', + # 'vpn/opt' => '/opt', 'xe' => '/usr/sbin' }