neutron: added test case to check connectivity using MTU sized frames
Properly configured setup must allow frames of network MTU size (as reported by Neutron net-mtu API extension) through the network. Sadly, Cirros images ship ping tools that don't support the -M argument that allows to set Don't Fragment flag, so the only way to enforce it is from outside the instance. Change-Id: I1dd0633e48dbc9b67cd79e610e959b514304a4b5 Related-Bug: #1623876
This commit is contained in:
parent
41383e6e22
commit
f9227c073f
@ -51,3 +51,23 @@ def get_unused_ip_addresses(ports_client, subnets_client,
|
||||
return addrs
|
||||
msg = "Insufficient IP addresses available"
|
||||
raise lib_exc.BadRequest(message=msg)
|
||||
|
||||
|
||||
def get_ping_payload_size(mtu, ip_version):
|
||||
"""Return the maximum size of ping payload that will fit into MTU."""
|
||||
if not mtu:
|
||||
return None
|
||||
if ip_version == 4:
|
||||
ip_header = 20
|
||||
icmp_header = 8
|
||||
else:
|
||||
ip_header = 40
|
||||
icmp_header = 4
|
||||
res = mtu - ip_header - icmp_header
|
||||
if res < 0:
|
||||
raise lib_exc.BadRequest(
|
||||
message='MTU = %(mtu)d is too low for IPv%(ip_version)d' % {
|
||||
'mtu': mtu,
|
||||
'ip_version': ip_version,
|
||||
})
|
||||
return res
|
||||
|
@ -26,6 +26,7 @@ from tempest.common import compute
|
||||
from tempest.common import image as common_image
|
||||
from tempest.common.utils import data_utils
|
||||
from tempest.common.utils.linux import remote_client
|
||||
from tempest.common.utils import net_utils
|
||||
from tempest.common import waiters
|
||||
from tempest import config
|
||||
from tempest import exceptions
|
||||
@ -495,9 +496,18 @@ class ScenarioTest(tempest.test.BaseTestCase):
|
||||
server_id, 'ACTIVE')
|
||||
|
||||
def ping_ip_address(self, ip_address, should_succeed=True,
|
||||
ping_timeout=None):
|
||||
ping_timeout=None, mtu=None):
|
||||
timeout = ping_timeout or CONF.validation.ping_timeout
|
||||
cmd = ['ping', '-c1', '-w1', ip_address]
|
||||
cmd = ['ping', '-c1', '-w1']
|
||||
|
||||
if mtu:
|
||||
cmd += [
|
||||
# don't fragment
|
||||
'-M', 'do',
|
||||
# ping receives just the size of ICMP payload
|
||||
'-s', str(net_utils.get_ping_payload_size(mtu, 4))
|
||||
]
|
||||
cmd.append(ip_address)
|
||||
|
||||
def ping():
|
||||
proc = subprocess.Popen(cmd,
|
||||
@ -525,7 +535,8 @@ class ScenarioTest(tempest.test.BaseTestCase):
|
||||
def check_vm_connectivity(self, ip_address,
|
||||
username=None,
|
||||
private_key=None,
|
||||
should_connect=True):
|
||||
should_connect=True,
|
||||
mtu=None):
|
||||
"""Check server connectivity
|
||||
|
||||
:param ip_address: server to test against
|
||||
@ -534,6 +545,7 @@ class ScenarioTest(tempest.test.BaseTestCase):
|
||||
:param should_connect: True/False indicates positive/negative test
|
||||
positive - attempt ping and ssh
|
||||
negative - attempt ping and fail if succeed
|
||||
:param mtu: network MTU to use for connectivity validation
|
||||
|
||||
:raises: AssertError if the result of the connectivity check does
|
||||
not match the value of the should_connect param
|
||||
@ -543,7 +555,8 @@ class ScenarioTest(tempest.test.BaseTestCase):
|
||||
else:
|
||||
msg = "ip address %s is reachable" % ip_address
|
||||
self.assertTrue(self.ping_ip_address(ip_address,
|
||||
should_succeed=should_connect),
|
||||
should_succeed=should_connect,
|
||||
mtu=mtu),
|
||||
msg=msg)
|
||||
if should_connect:
|
||||
# no need to check ssh for negative connectivity
|
||||
@ -551,7 +564,7 @@ class ScenarioTest(tempest.test.BaseTestCase):
|
||||
|
||||
def check_public_network_connectivity(self, ip_address, username,
|
||||
private_key, should_connect=True,
|
||||
msg=None, servers=None):
|
||||
msg=None, servers=None, mtu=None):
|
||||
# The target login is assumed to have been configured for
|
||||
# key-based authentication by cloud-init.
|
||||
LOG.debug('checking network connections to IP %s with user: %s' %
|
||||
@ -560,7 +573,8 @@ class ScenarioTest(tempest.test.BaseTestCase):
|
||||
self.check_vm_connectivity(ip_address,
|
||||
username,
|
||||
private_key,
|
||||
should_connect=should_connect)
|
||||
should_connect=should_connect,
|
||||
mtu=mtu)
|
||||
except Exception:
|
||||
ex_msg = 'Public network connectivity check failed'
|
||||
if msg:
|
||||
|
@ -183,7 +183,7 @@ class TestNetworkBasicOps(manager.NetworkScenarioTest):
|
||||
|
||||
def check_public_network_connectivity(
|
||||
self, should_connect=True, msg=None,
|
||||
should_check_floating_ip_status=True):
|
||||
should_check_floating_ip_status=True, mtu=None):
|
||||
"""Verifies connectivty to a VM via public network and floating IP
|
||||
|
||||
and verifies floating IP has resource status is correct.
|
||||
@ -195,6 +195,7 @@ class TestNetworkBasicOps(manager.NetworkScenarioTest):
|
||||
to indicate the context of the failure
|
||||
:param should_check_floating_ip_status: bool. should status of
|
||||
floating_ip be checked or not
|
||||
:param mtu: int. MTU network to use for connectivity validation
|
||||
"""
|
||||
ssh_login = CONF.validation.image_ssh_user
|
||||
floating_ip, server = self.floating_ip_tuple
|
||||
@ -210,7 +211,7 @@ class TestNetworkBasicOps(manager.NetworkScenarioTest):
|
||||
# call the common method in the parent class
|
||||
super(TestNetworkBasicOps, self).check_public_network_connectivity(
|
||||
ip_address, ssh_login, private_key, should_connect, msg,
|
||||
self.servers)
|
||||
self.servers, mtu=mtu)
|
||||
|
||||
def _disassociate_floating_ips(self):
|
||||
floating_ip, server = self.floating_ip_tuple
|
||||
@ -410,6 +411,16 @@ class TestNetworkBasicOps(manager.NetworkScenarioTest):
|
||||
msg="after re-associate "
|
||||
"floating ip")
|
||||
|
||||
@test.idempotent_id('b158ea55-472e-4086-8fa9-c64ac0c6c1d0')
|
||||
@testtools.skipUnless(test.is_extension_enabled('net-mtu', 'network'),
|
||||
'No way to calculate MTU for networks')
|
||||
@test.services('compute', 'network')
|
||||
def test_mtu_sized_frames(self):
|
||||
"""Validate that network MTU sized frames fit through."""
|
||||
self._setup_network_and_servers()
|
||||
self.check_public_network_connectivity(
|
||||
should_connect=True, mtu=self.network['mtu'])
|
||||
|
||||
@test.idempotent_id('1546850e-fbaa-42f5-8b5f-03d8a6a95f15')
|
||||
@testtools.skipIf(CONF.baremetal.driver_enabled,
|
||||
'Baremetal relies on a shared physical network.')
|
||||
|
33
tempest/tests/common/utils/test_net_utils.py
Normal file
33
tempest/tests/common/utils/test_net_utils.py
Normal file
@ -0,0 +1,33 @@
|
||||
# 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 mock
|
||||
|
||||
from tempest.common.utils import net_utils
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
from tempest.tests import base
|
||||
|
||||
|
||||
class TestGetPingPayloadSize(base.TestCase):
|
||||
|
||||
def test_ipv4(self):
|
||||
self.assertEqual(1422, net_utils.get_ping_payload_size(1450, 4))
|
||||
|
||||
def test_ipv6(self):
|
||||
self.assertEqual(1406, net_utils.get_ping_payload_size(1450, 6))
|
||||
|
||||
def test_too_low_mtu(self):
|
||||
self.assertRaises(
|
||||
lib_exc.BadRequest, net_utils.get_ping_payload_size, 10, 4)
|
||||
|
||||
def test_None(self):
|
||||
self.assertIsNone(net_utils.get_ping_payload_size(None, mock.Mock()))
|
Loading…
Reference in New Issue
Block a user