From 34995c649ad342bc4ad0df81649cf8fd5555160d Mon Sep 17 00:00:00 2001 From: Dima Shulyak Date: Thu, 27 Mar 2014 13:14:21 +0200 Subject: [PATCH] Prepare network_checker for ci Vagrant will spawn virtualbox vm with 3 interfaces with 2 of them in same network (which will be handy for some debug) eth1 and eth2 will be controlled by dhcp server in same network This approach has couple of requirements: 1. virtualbox installed on jenkins node 2. vagrant installed on jenkins node 3. added precise64 box for vagrant vagrant up - will create vm and install all necessery packages to run tests: vagrant ssh -c 'sudo py.test /vagrant/' will run tests for network_checker and dhcpchecker exit status will be non-zero in case of errors sudo is required for capturing data on ifaces To make docker approach to work: 1. Share linux drivers for docker containter (netchecker uses modprobe 8021q) 2. setup dnsmasq on same bridge which this docker image is using Tests can be started on local machine with help of vde switch: py.test --vde ./ This will create vde_switch and tap ifaces (tap11, tap12) Related to blueprint fuel-network-checks-ci Change-Id: Ieefca56f31db988a610870710ec9cb0ad1a0d6a6 --- network_checker/Dockerfile | 10 +++ network_checker/Vagrantfile | 35 +++------- network_checker/conftest.py | 44 ++++++++++++ .../dhcp_checker/tests/system/tests.py | 25 +++---- .../net_check/tests/test_net_check.py | 63 +++++++++++------- network_checker/net_check/tests/vlan.pcap | Bin 6864 -> 0 bytes 6 files changed, 110 insertions(+), 67 deletions(-) create mode 100644 network_checker/Dockerfile create mode 100644 network_checker/conftest.py delete mode 100644 network_checker/net_check/tests/vlan.pcap diff --git a/network_checker/Dockerfile b/network_checker/Dockerfile new file mode 100644 index 0000000000..9c45ccaf59 --- /dev/null +++ b/network_checker/Dockerfile @@ -0,0 +1,10 @@ +FROM phusion/baseimage +ENV ARCH amd64 +ENV DIST precise +RUN echo 'deb http://fuel-repository.mirantis.com/fwm/5.0/ubuntu precise main' >> /etc/apt/sources.list +RUN apt-get -q update +RUN apt-get -y --force-yes install cliff-tablib python-pyparsing python-pypcap scapy python-pip wget openssh-server +RUN pip install pytest mock +RUN sudo locale-gen en_US.UTF-8 + +RUN mkdir -p /app diff --git a/network_checker/Vagrantfile b/network_checker/Vagrantfile index 09d7e9e95b..42043fed6a 100644 --- a/network_checker/Vagrantfile +++ b/network_checker/Vagrantfile @@ -6,39 +6,20 @@ VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - config.vm.box = "centos" - - config.vm.box_url = "http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-i386-v20130427.box" + config.vm.box = "precise64" config.vm.provider "virtualbox" do |v| v.customize ["modifyvm", :id, "--memory", 348] end config.vm.define :develop do |config| - config.vm.network :private_network, ip: "192.168.0.2" - config.vm.network :private_network, ip: "10.10.0.5" + config.vm.network :private_network, ip: '10.100.0.0', type: "dhcp" + config.vm.network :private_network, ip: '10.200.0.0', type: 'dhcp' + config.vm.provision :shell, :inline => "echo 'deb http://fuel-repository.mirantis.com/fwm/5.0/ubuntu precise main' >> /etc/apt/sources.list" + config.vm.provision :shell, :inline => "sudo apt-get update" + config.vm.provision :shell, :inline => "sudo apt-get -y --force-yes install cliff-tablib python-pyparsing python-pypcap scapy python-pip vde2" + config.vm.provision :shell, :inline => "sudo pip install pytest mock" + config.vm.provision :shell, :inline => "cd /vagrant && sudo python setup.py develop" end - config.vm.define :dhcp2 do |config| - config.vm.network :private_network, ip: "10.10.0.8" - config.vm.provision :shell, :inline => "sudo yum -y install dhcp" - config.vm.provision :shell, :inline => "sudo route add -host 255.255.255.255 dev eth1" - config.vm.provision :shell, :inline => "sudo cp /vagrant/test_utils/dhcpd.conf.sample2 /etc/dhcp/dhcpd.conf" - config.vm.provision :shell, :inline => "sudo /usr/sbin/dhcpd eth1" - end - - config.vm.define :dhcp_relay do |config| - config.vm.network :private_network, ip: "10.10.0.10" - config.vm.provision :shell, :inline => "sudo yum -y install dhcp" - config.vm.provision :shell, :inline => "cp /vagrant/test_utils/dhcp_relay.conf /etc/sysconfig/dhcrelay" - config.vm.provision :shell, :inline => "service dhcrelay start" - end - - config.vm.define :dhcp1 do |config| - config.vm.network :private_network, ip: "192.168.0.5" - config.vm.provision :shell, :inline => "sudo yum -y install dhcp" - config.vm.provision :shell, :inline => "sudo route add -host 255.255.255.255 dev eth1" - config.vm.provision :shell, :inline => "sudo cp /vagrant/test_utils/dhcpd.conf.sample /etc/dhcp/dhcpd.conf" - config.vm.provision :shell, :inline => "sudo /usr/sbin/dhcpd eth1" - end end diff --git a/network_checker/conftest.py b/network_checker/conftest.py new file mode 100644 index 0000000000..c8aabfaa46 --- /dev/null +++ b/network_checker/conftest.py @@ -0,0 +1,44 @@ +# Copyright 2014 Mirantis, 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. + +import os + + +PIDFILE = '/tmp/vde_network_checker' +IFACES = ['tap11', 'tap12'] + + +def pytest_addoption(parser): + parser.addoption("--vde", action='store_true', default=False, + help="Use vde switch for network verification.") + + +def pytest_configure(config): + if config.getoption('vde'): + base = 'vde_switch -p {pidfile} -d'.format(pidfile=PIDFILE) + command = [base] + taps = ['-tap {tap}'.format(tap=tap) for tap in IFACES] + full_command = command + taps + os.system(' '.join(full_command)) + for tap in IFACES: + os.system('ifconfig {tap} up'.format(tap=tap)) + os.environ['NET_CHECK_IFACE_1'] = IFACES[0] + os.environ['NET_CHECK_IFACE_2'] = IFACES[1] + + +def pytest_unconfigure(config): + if os.path.exists(PIDFILE): + with open(PIDFILE) as f: + pid = f.read().strip() + os.kill(int(pid), 15) diff --git a/network_checker/dhcp_checker/tests/system/tests.py b/network_checker/dhcp_checker/tests/system/tests.py index 00934cc696..02cca9c29d 100644 --- a/network_checker/dhcp_checker/tests/system/tests.py +++ b/network_checker/dhcp_checker/tests/system/tests.py @@ -24,26 +24,21 @@ from dhcp_checker import utils class TestDhcpServers(unittest.TestCase): - def test_dhcp_server_on_eth0(self): - """Test verifies dhcp server on eth0 iface - """ - response = api.check_dhcp_on_eth('eth0', 2) - self.assertEqual(len(response), 1) - self.assertEqual(response[0]['server_ip'], '10.0.2.2') - def test_dhcp_server_on_eth1(self): """Test verifies dhcp server on eth1 iface """ response = api.check_dhcp_on_eth('eth1', 2) self.assertEqual(len(response), 1) - self.assertEqual(response[0]['server_ip'], '192.168.0.5') + # we need to guarantee that received answer has server_ip + # but dont want to check its real address + self.assertTrue(response[0]['server_ip']) def test_dhcp_server_on_eth2(self): """Test verifies dhcp server on eth2 iface """ response = api.check_dhcp_on_eth('eth2', 2) self.assertEqual(len(response), 1) - self.assertEqual(response[0]['server_ip'], '10.10.0.8') + self.assertTrue(response[0]['server_ip']) class TestDhcpUtils(unittest.TestCase): @@ -83,7 +78,7 @@ class TestDhcpWithNetworkDown(unittest.TestCase): response = api.check_dhcp_on_eth(iface, 2) self.assertEqual(len(response), 1) - self.assertEqual(response[0]['server_ip'], '10.10.0.8') + self.assertTrue(response[0]['server_ip']) self.assertEqual(manager.pre_iface_state, 'DOWN') self.assertEqual(manager.iface_state, 'UP') self.assertEqual(manager.post_iface_state, 'DOWN') @@ -96,7 +91,7 @@ class TestDhcpWithNetworkDown(unittest.TestCase): response = api.check_dhcp_on_eth(iface, 2) self.assertEqual(len(response), 1) - self.assertEqual(response[0]['server_ip'], '10.0.2.2') + self.assertTrue(response[0]['server_ip']) self.assertEqual(manager.pre_iface_state, 'UP') self.assertEqual(manager.iface_state, 'UP') self.assertEqual(manager.post_iface_state, 'UP') @@ -116,15 +111,15 @@ class TestDhcpWithNetworkDown(unittest.TestCase): class TestMainFunctions(unittest.TestCase): def test_with_vlans(self): - config = {'eth0': (100, 101), 'eth1': (103, 105), + config = {'eth1': (103, 105), 'eth2': range(106, 120)} result = api.check_dhcp_with_vlans(config) - self.assertEqual(len(list(result)), 3) + self.assertEqual(len(list(result)), 2) def test_with_duplicated_with_repeat(self): - ifaces = ['eth0', 'eth1', 'eth2'] + ifaces = ['eth1', 'eth2'] result = api.check_dhcp(ifaces, repeat=3) - self.assertEqual(len(list(result)), 3) + self.assertEqual(len(list(result)), 2) if __name__ == '__main__': diff --git a/network_checker/net_check/tests/test_net_check.py b/network_checker/net_check/tests/test_net_check.py index 3e7db57669..b57ecbddec 100644 --- a/network_checker/net_check/tests/test_net_check.py +++ b/network_checker/net_check/tests/test_net_check.py @@ -29,10 +29,11 @@ from net_check import api class BaseListenerTestCase(unittest.TestCase): def setUp(self, config=None): + self.iface = os.environ.get('NET_CHECK_IFACE_1', 'eth1') default_config = { "src": "1.0.0.0", "ready_port": None, "ready_address": "localhost", "dst": "1.0.0.0", - "interfaces": {"eth0": "0,100,100,101,102,103,104,105,106,107"}, + "interfaces": {self.iface: "0,100,101,102,103,104,105,106,107"}, "action": "listen", "cookie": "Nailgun:", "dport": 31337, "sport": 31337, "src_mac": None, "dump_file": "/var/tmp/net-probe-dump" @@ -72,34 +73,48 @@ class BaseListenerTestCase(unittest.TestCase): os.unlink(self.config['dump_file']) -class TestCaseListenerPcapFile(BaseListenerTestCase): +class TestCaseListenerPcap(BaseListenerTestCase): def send_packets(self): - directory_path = os.path.dirname(__file__) - scapy_data = scapy.rdpcap(os.path.join(directory_path, 'vlan.pcap')) - for p in scapy_data: - scapy.sendp(p, iface='eth0') + for vlan in self.config['interfaces'][self.iface].split(','): + p = self.get_packet(vlan) + for i in xrange(5): + scapy.sendp(p, iface=self.iface) + + def get_packet(self, vlan): + normal_data = 'Nailgun:{iface} 1'.format(iface=self.iface) + p = scapy.Ether(src='64:0b:36:0e:0a:b7', + dst="ff:ff:ff:ff:ff:ff") + if int(vlan) > 0: + p = p / scapy.Dot1Q(vlan=int(vlan)) + message_len = len(normal_data) + 8 + p = p / scapy.IP(src=self.config['src'], dst=self.config['dst']) + p = p / scapy.UDP(sport=self.config['sport'], + dport=self.config['dport'], + len=message_len) / normal_data + return p def test_listener_pcap_file(self): with open(self.config['dump_file'], 'r') as f: data = json.loads(f.read()) - self.assertEqual(data, {u'eth0': { - u'102': {u'1': [u'eth0'], u'2': [u'eth0']}, - u'103': {u'1': [u'eth0'], u'2': [u'eth0']}, - u'100': {u'1': [u'eth0'], u'2': [u'eth0']}, - u'101': {u'1': [u'eth0'], u'2': [u'eth0']}, - u'106': {u'1': [u'eth0'], u'2': [u'eth0']}, - u'107': {u'1': [u'eth0'], u'2': [u'eth0']}, - u'104': {u'1': [u'eth0'], u'2': [u'eth0']}, - u'105': {u'1': [u'eth0'], u'2': [u'eth0']}}}) + self.assertEqual(data, {self.iface: { + u'0': {u'1': [self.iface]}, + u'102': {u'1': [self.iface]}, + u'103': {u'1': [self.iface]}, + u'100': {u'1': [self.iface]}, + u'101': {u'1': [self.iface]}, + u'106': {u'1': [self.iface]}, + u'107': {u'1': [self.iface]}, + u'104': {u'1': [self.iface]}, + u'105': {u'1': [self.iface]}}}) class TestCaseListenerCorruptedData(BaseListenerTestCase): def send_packets(self): - normal_data = 'Nailgun:eth0 2' + normal_data = 'Nailgun:{iface} 2'.format(iface=self.iface) corrupted_data = normal_data + '7h 7\00\00\00' message_len = len(normal_data) + 8 p = scapy.Ether(src=self.config['src_mac'], @@ -109,26 +124,24 @@ class TestCaseListenerCorruptedData(BaseListenerTestCase): dport=self.config['dport'], len=message_len) / corrupted_data for i in xrange(5): - scapy.sendp(p, iface='eth0') + scapy.sendp(p, iface=self.iface) def test_listener_corrupted_data(self): with open(self.config['dump_file'], 'r') as f: data = json.loads(f.read()) - self.assertEqual(data, {u'eth0': {u'0': {u'2': [u'eth0']}}}) + self.assertEqual(data, {self.iface: {u'0': {u'2': [self.iface]}}}) class TestNetCheckSender(unittest.TestCase): def setUp(self): - directory_path = os.path.dirname(__file__) - self.scapy_data = scapy.rdpcap(os.path.join(directory_path, - 'vlan.pcap')) + self.iface = os.environ.get('NET_CHECK_IFACE_1', 'eth1') self.config = { "src": "1.0.0.0", "ready_port": 31338, "ready_address": "localhost", "dst": "1.0.0.0", - "interfaces": {"eth0": "0,100,101,102,106,107,108"}, + "interfaces": {self.iface: "0,100,101,102,106,107,108"}, "action": "listen", "cookie": "Nailgun:", "dport": 31337, "sport": 31337, "src_mac": None, @@ -137,8 +150,8 @@ class TestNetCheckSender(unittest.TestCase): } def start_pcap_listener(self): - self.pcap_listener = pcap.pcap('eth0') - self.vlan_pcap_listener = pcap.pcap('eth0') + self.pcap_listener = pcap.pcap(self.iface) + self.vlan_pcap_listener = pcap.pcap(self.iface) filter_string = 'udp and dst port {0}'.format(self.config['dport']) self.vlan_pcap_listener.setfilter('vlan and {0}'.format(filter_string)) self.pcap_listener.setfilter(filter_string) @@ -173,5 +186,5 @@ class TestNetCheckSender(unittest.TestCase): time.sleep(3) self.sender.join() - expected_vlans = set(self.config['interfaces']['eth0'].split(',')) + expected_vlans = set(self.config['interfaces'][self.iface].split(',')) self.assertEqual(expected_vlans, self.received_vlans) diff --git a/network_checker/net_check/tests/vlan.pcap b/network_checker/net_check/tests/vlan.pcap deleted file mode 100644 index 99d523202d027ff387dba287d3eec95a59d003a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6864 zcmb{0Pe>GD9LDi?%?-&?Iw%OWltD&GtkQ;r)DlEW5V3gauwbC0Q@RE!h8=26j7TMd zM97c`LOTRXhZdv2KSTv9f+a{)A_)sZmKnPmZ95Ge%=evm;XClc49t8E&-`ZKd8fyQ z@281$@%Kn3g>a=VcIKFY&=Z#^O`TgS`RB+G1(mU%VIitA#0eqxTeteRUmCNXuvV+t zYKq*hyQj~Ybv4bQBh^i{-d)9(b>#(TMc#0h_bq2#{lHm~tTZh9gL$0wJfE|66mr%h zA7`!BDXnVVxgT%gtZiY=a^K{vraPR~9#vX~Zuaq6&YJnmS>s(u~f9UtPX z#}!!{zoCZd^8a?LzIENI$m&)vgPirv;4DuAXU(;6*6WX)RTkr{;8)I?OK_HZb2c)o zp46=7725j=>-H4vxp(eX|IEE*RUPK6dw$N^QqEa{0B5bg0s4#oYg$T zS)-pgtNoN@Z^)i|=WZp*eyW1AdV`!bX>iuli=4H4R%tnRD@pe6<~gf!k+W{Ba8}7L z&T^TO{Rw;Sox6f0dw(NmB`