Update OVS router namespace test cases
- Split test cases in sequencial sub-tests - Treat dvr_no_external as dvr Depends-On: https://review.opendev.org/c/x/devstack-plugin-tobiko/+/846357 Change-Id: Ief941ae2583ce4bafedf59db00ec21ebb93ae521
This commit is contained in:
parent
164df84c22
commit
1fe9e0a550
@ -85,14 +85,21 @@ def get_l3_agent_mode(
|
|||||||
l3_agent_conf_path: str,
|
l3_agent_conf_path: str,
|
||||||
default='legacy',
|
default='legacy',
|
||||||
connection: sh.ShellConnectionType = None) -> str:
|
connection: sh.ShellConnectionType = None) -> str:
|
||||||
|
connection = sh.shell_connection(connection)
|
||||||
|
LOG.debug(f"Read L3 agent mode from file '{l3_agent_conf_path}' on host "
|
||||||
|
f" '{connection.hostname}'...")
|
||||||
with sh.open_file(l3_agent_conf_path, 'rt',
|
with sh.open_file(l3_agent_conf_path, 'rt',
|
||||||
connection=connection) as fd:
|
connection=connection) as fd:
|
||||||
|
LOG.debug(f"Parse ini file '{l3_agent_conf_path}'...")
|
||||||
content = tobiko.parse_ini_file(fd)
|
content = tobiko.parse_ini_file(fd)
|
||||||
try:
|
try:
|
||||||
return content['DEFAULT', 'agent_mode']
|
agent_mode = content['DEFAULT', 'agent_mode']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
LOG.error(f"agent_mode not found in file {l3_agent_conf_path}")
|
LOG.error(f"agent_mode not found in file {l3_agent_conf_path}")
|
||||||
return default
|
agent_mode = default
|
||||||
|
LOG.debug(f"Got L3 agent mode from file '{l3_agent_conf_path}' "
|
||||||
|
f"on host '{connection.hostname}': '{agent_mode}'")
|
||||||
|
return agent_mode
|
||||||
|
|
||||||
|
|
||||||
class NetworkingAgentFixture(tobiko.SharedFixture):
|
class NetworkingAgentFixture(tobiko.SharedFixture):
|
||||||
|
@ -35,6 +35,7 @@ assert_ovn_unsupported_dhcp_option_messages = (
|
|||||||
get_hosts_namespaces = _namespace.get_hosts_namespaces
|
get_hosts_namespaces = _namespace.get_hosts_namespaces
|
||||||
assert_namespace_in_hosts = _namespace.assert_namespace_in_hosts
|
assert_namespace_in_hosts = _namespace.assert_namespace_in_hosts
|
||||||
assert_namespace_not_in_hosts = _namespace.assert_namespace_not_in_hosts
|
assert_namespace_not_in_hosts = _namespace.assert_namespace_not_in_hosts
|
||||||
|
wait_for_namespace_in_hosts = _namespace.wait_for_namespace_in_hosts
|
||||||
|
|
||||||
list_nodes_processes = _sh.list_nodes_processes
|
list_nodes_processes = _sh.list_nodes_processes
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
import json
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
import tobiko
|
import tobiko
|
||||||
@ -36,23 +37,59 @@ def get_hosts_namespaces(hostnames: typing.Iterable[str] = None,
|
|||||||
return namespaces
|
return namespaces
|
||||||
|
|
||||||
|
|
||||||
def assert_namespace_in_hosts(namespace: str,
|
def wait_for_namespace_in_hosts(*namespaces: str,
|
||||||
|
hostnames: typing.Iterable[str] = None,
|
||||||
|
timeout: tobiko.Seconds = None,
|
||||||
|
count: int = None,
|
||||||
|
interval: tobiko.Seconds = None,
|
||||||
|
present=True,
|
||||||
|
**params):
|
||||||
|
for attempt in tobiko.retry(timeout=timeout,
|
||||||
|
count=count,
|
||||||
|
interval=interval,
|
||||||
|
default_timeout=60.,
|
||||||
|
default_interval=5.):
|
||||||
|
try:
|
||||||
|
if present:
|
||||||
|
assert_namespace_in_hosts(*namespaces,
|
||||||
|
hostnames=hostnames,
|
||||||
|
**params)
|
||||||
|
else:
|
||||||
|
assert_namespace_not_in_hosts(*namespaces,
|
||||||
|
hostnames=hostnames,
|
||||||
|
**params)
|
||||||
|
except tobiko.FailureException: # type: ignore
|
||||||
|
if attempt.is_last:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def assert_namespace_in_hosts(*namespaces: str,
|
||||||
hostnames: typing.Iterable[str] = None,
|
hostnames: typing.Iterable[str] = None,
|
||||||
**params):
|
**params):
|
||||||
namespaces = get_hosts_namespaces(hostnames=hostnames, **params)
|
actual_namespaces = get_hosts_namespaces(hostnames=hostnames,
|
||||||
actual_hostnames = set(_hostname
|
**params)
|
||||||
for _hostnames in namespaces.values()
|
missing = set(namespaces) - set(actual_namespaces)
|
||||||
for _hostname in _hostnames)
|
if missing:
|
||||||
tobiko.get_test_case().assertIn(
|
actual_hostnames = sorted(set(
|
||||||
namespace, set(namespaces),
|
_hostname
|
||||||
f"Namespace {namespace!r} not in hosts {actual_hostnames!r}")
|
for _hostnames in actual_namespaces.values()
|
||||||
|
for _hostname in _hostnames))
|
||||||
|
tobiko.fail(f"Network namespace(s) {sorted(missing)} missing in "
|
||||||
|
f"host(s) {actual_hostnames!r}")
|
||||||
|
|
||||||
|
|
||||||
def assert_namespace_not_in_hosts(namespace: str,
|
def assert_namespace_not_in_hosts(*namespaces: str,
|
||||||
hostnames: typing.Iterable[str] = None,
|
hostnames: typing.Iterable[str] = None,
|
||||||
**params):
|
**params):
|
||||||
namespaces = get_hosts_namespaces(hostnames=hostnames, **params)
|
unexpected_namespaces = collections.defaultdict(list)
|
||||||
actual_hostnames = namespaces.get(namespace)
|
actual_namespaces = get_hosts_namespaces(hostnames=hostnames, **params)
|
||||||
tobiko.get_test_case().assertNotIn(
|
for namespace, hostnames in actual_namespaces.items():
|
||||||
namespace, set(namespaces),
|
if namespace in sorted(set(namespaces)):
|
||||||
f"Namespace {namespace!r} in hosts: {actual_hostnames!r}")
|
for hostname in hostnames:
|
||||||
|
unexpected_namespaces[hostname].append(namespace)
|
||||||
|
if unexpected_namespaces:
|
||||||
|
dump = json.dumps(unexpected_namespaces, indent=4, sort_keys=True)
|
||||||
|
tobiko.fail(f"Unexpected network namespace(s) found in "
|
||||||
|
f"host(s):\n{dump}")
|
||||||
|
@ -46,10 +46,19 @@ OpenstackGroupNamesType = typing.Union[OpenstackGroupNameType, typing.Iterable[
|
|||||||
OpenstackGroupNameType]]
|
OpenstackGroupNameType]]
|
||||||
|
|
||||||
|
|
||||||
def list_openstack_nodes(topology: 'OpenStackTopology' = None,
|
HostAddressType = typing.Union[str, netaddr.IPAddress]
|
||||||
|
|
||||||
|
|
||||||
|
def list_openstack_nodes(addresses: typing.Iterable[netaddr.IPAddress] = None,
|
||||||
group: OpenstackGroupNamesType = None,
|
group: OpenstackGroupNamesType = None,
|
||||||
hostnames=None, **kwargs):
|
hostnames: typing.Iterable[str] = None,
|
||||||
topology = topology or get_openstack_topology()
|
topology: 'OpenStackTopology' = None,
|
||||||
|
**kwargs) \
|
||||||
|
-> tobiko.Selection['OpenStackTopologyNode']:
|
||||||
|
if topology is None:
|
||||||
|
topology = get_openstack_topology()
|
||||||
|
|
||||||
|
nodes: tobiko.Selection[OpenStackTopologyNode]
|
||||||
if group is None:
|
if group is None:
|
||||||
nodes = topology.nodes
|
nodes = topology.nodes
|
||||||
elif isinstance(group, str):
|
elif isinstance(group, str):
|
||||||
@ -60,7 +69,40 @@ def list_openstack_nodes(topology: 'OpenStackTopology' = None,
|
|||||||
assert isinstance(group, abc.Iterable)
|
assert isinstance(group, abc.Iterable)
|
||||||
nodes = topology.get_groups(groups=group)
|
nodes = topology.get_groups(groups=group)
|
||||||
|
|
||||||
if hostnames:
|
return select_openstack_nodes(nodes,
|
||||||
|
addresses=addresses,
|
||||||
|
hostnames=hostnames,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def split_addresses_and_names(*hosts: HostAddressType) \
|
||||||
|
-> typing.Tuple[typing.Set[netaddr.IPAddress], typing.Set[str]]:
|
||||||
|
addresses = set()
|
||||||
|
hostnames = set()
|
||||||
|
for host in hosts:
|
||||||
|
try:
|
||||||
|
addresses.add(netaddr.IPAddress(host))
|
||||||
|
except netaddr.AddrFormatError:
|
||||||
|
hostnames.add(host)
|
||||||
|
return addresses, hostnames
|
||||||
|
|
||||||
|
|
||||||
|
def select_openstack_nodes(
|
||||||
|
nodes: tobiko.Selection['OpenStackTopologyNode'],
|
||||||
|
addresses: typing.Iterable[netaddr.IPAddress] = None,
|
||||||
|
hostnames: typing.Iterable[str] = None,
|
||||||
|
**kwargs) \
|
||||||
|
-> tobiko.Selection['OpenStackTopologyNode']:
|
||||||
|
for selector in addresses, hostnames:
|
||||||
|
if selector is not None and not selector:
|
||||||
|
return tobiko.Selection()
|
||||||
|
|
||||||
|
if addresses is not None:
|
||||||
|
_addresses = set(addresses)
|
||||||
|
nodes = nodes.select(
|
||||||
|
lambda node: bool(set(node.addresses) & _addresses))
|
||||||
|
|
||||||
|
if hostnames is not None:
|
||||||
names = {node_name_from_hostname(hostname)
|
names = {node_name_from_hostname(hostname)
|
||||||
for hostname in hostnames}
|
for hostname in hostnames}
|
||||||
nodes = nodes.select(lambda node: node.name in names)
|
nodes = nodes.select(lambda node: node.name in names)
|
||||||
@ -70,8 +112,18 @@ def list_openstack_nodes(topology: 'OpenStackTopology' = None,
|
|||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
def find_openstack_node(topology=None, unique=False, **kwargs):
|
def find_openstack_node(addresses: typing.Iterable[netaddr.IPAddress] = None,
|
||||||
nodes = list_openstack_nodes(topology=topology, **kwargs)
|
group: OpenstackGroupNamesType = None,
|
||||||
|
hostnames: typing.Iterable[str] = None,
|
||||||
|
topology: 'OpenStackTopology' = None,
|
||||||
|
unique=False,
|
||||||
|
**kwargs) \
|
||||||
|
-> 'OpenStackTopologyNode':
|
||||||
|
nodes = list_openstack_nodes(topology=topology,
|
||||||
|
addresses=addresses,
|
||||||
|
group=group,
|
||||||
|
hostnames=hostnames,
|
||||||
|
**kwargs)
|
||||||
if unique:
|
if unique:
|
||||||
return nodes.unique
|
return nodes.unique
|
||||||
else:
|
else:
|
||||||
@ -156,12 +208,13 @@ class OpenStackTopologyNode(object):
|
|||||||
_podman_client = None
|
_podman_client = None
|
||||||
|
|
||||||
def __init__(self, topology, name: str, ssh_client: ssh.SSHClientFixture,
|
def __init__(self, topology, name: str, ssh_client: ssh.SSHClientFixture,
|
||||||
addresses: typing.List[netaddr.IPAddress], hostname: str):
|
addresses: typing.Iterable[netaddr.IPAddress], hostname: str):
|
||||||
self._topology = weakref.ref(topology)
|
self._topology = weakref.ref(topology)
|
||||||
self.name = name
|
self.name = name
|
||||||
self.ssh_client = ssh_client
|
self.ssh_client = ssh_client
|
||||||
self.groups: typing.Set[str] = set()
|
self.groups: typing.Set[str] = set()
|
||||||
self.addresses: typing.List[netaddr.IPAddress] = list(addresses)
|
self.addresses: tobiko.Selection[netaddr.IPAddress] = tobiko.select(
|
||||||
|
addresses)
|
||||||
self.hostname: str = hostname
|
self.hostname: str = hostname
|
||||||
|
|
||||||
_connection: typing.Optional[sh.ShellConnection] = None
|
_connection: typing.Optional[sh.ShellConnection] = None
|
||||||
@ -618,7 +671,7 @@ def get_openstack_version():
|
|||||||
DEFAULT_TOPOLOGY_CLASS = OpenStackTopology
|
DEFAULT_TOPOLOGY_CLASS = OpenStackTopology
|
||||||
|
|
||||||
|
|
||||||
def node_name_from_hostname(hostname):
|
def node_name_from_hostname(hostname: str):
|
||||||
return hostname.split('.', 1)[0].lower()
|
return hostname.split('.', 1)[0].lower()
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import collections
|
||||||
|
import json
|
||||||
|
import re
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -24,8 +27,8 @@ import tobiko
|
|||||||
from tobiko import config
|
from tobiko import config
|
||||||
from tobiko.shell import ping
|
from tobiko.shell import ping
|
||||||
from tobiko.shell import ip
|
from tobiko.shell import ip
|
||||||
from tobiko.shell import ssh
|
|
||||||
from tobiko.openstack import neutron
|
from tobiko.openstack import neutron
|
||||||
|
from tobiko.openstack import nova
|
||||||
from tobiko.openstack import stacks
|
from tobiko.openstack import stacks
|
||||||
from tobiko.openstack import topology
|
from tobiko.openstack import topology
|
||||||
|
|
||||||
@ -150,73 +153,185 @@ class L3HARouterTest(RouterTest):
|
|||||||
backup_agent['host'], state="backup")
|
backup_agent['host'], state="backup")
|
||||||
|
|
||||||
|
|
||||||
class DistributedRouterStackFixture(stacks.RouterStackFixture):
|
class RouterNamespaceTestBase:
|
||||||
|
|
||||||
|
server_stack = tobiko.required_fixture(stacks.CirrosServerStackFixture)
|
||||||
|
|
||||||
|
host_groups = ['overcloud', 'compute', 'controller']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hostnames(self) -> typing.List[str]:
|
||||||
|
return sorted(
|
||||||
|
node.hostname
|
||||||
|
for node in topology.list_openstack_nodes(group=self.host_groups))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def router_stack(self) -> stacks.RouterStackFixture:
|
||||||
|
return self.network_stack.gateway_stack
|
||||||
|
|
||||||
|
@property
|
||||||
|
def network_stack(self) -> stacks.NetworkStackFixture:
|
||||||
|
return self.server_stack.network_stack
|
||||||
|
|
||||||
|
@property
|
||||||
|
def router_id(self) -> str:
|
||||||
|
return self.router_stack.router_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def router_details(self) -> neutron.RouterType:
|
||||||
|
return self.router_stack.router_details
|
||||||
|
|
||||||
|
@property
|
||||||
|
def router_namespace(self) -> str:
|
||||||
|
return neutron.get_ovs_router_namespace(self.router_id)
|
||||||
|
|
||||||
|
|
||||||
|
@neutron.skip_unless_is_ovs()
|
||||||
|
class OvsRouterNamespaceTest(RouterNamespaceTestBase, testtools.TestCase):
|
||||||
|
|
||||||
|
def test_router_namespace(self):
|
||||||
|
"""Check router namespace is being created on cloud hosts
|
||||||
|
|
||||||
|
When A VM running in a compute node is expected to route
|
||||||
|
packages to a router, a network namespace is expected to exist on
|
||||||
|
compute name that is named after the router ID
|
||||||
|
"""
|
||||||
|
ping.assert_reachable_hosts([self.server_stack.floating_ip_address])
|
||||||
|
topology.assert_namespace_in_hosts(self.router_namespace,
|
||||||
|
hostnames=self.hostnames)
|
||||||
|
|
||||||
|
|
||||||
|
@neutron.skip_if_missing_networking_extensions('dvr')
|
||||||
|
class DvrRouterStackFixture(stacks.RouterStackFixture):
|
||||||
distributed = True
|
distributed = True
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.ovn_migration
|
class DvrNetworkStackFixture(stacks.NetworkStackFixture):
|
||||||
class RouterNamespaceTest(testtools.TestCase):
|
gateway_stack = tobiko.required_fixture(DvrRouterStackFixture,
|
||||||
|
setup=False)
|
||||||
|
|
||||||
server_stack = tobiko.required_fixture(stacks.CirrosServerStackFixture)
|
|
||||||
distributed_router_stack = (
|
|
||||||
tobiko.required_fixture(DistributedRouterStackFixture))
|
|
||||||
|
|
||||||
@neutron.skip_unless_is_ovn()
|
class DvrServerStackFixture(stacks.CirrosServerStackFixture):
|
||||||
def test_router_namespace_on_ovn(self):
|
network_stack = tobiko.required_fixture(DvrNetworkStackFixture,
|
||||||
"""Check router namespace is being created on compute host
|
setup=False)
|
||||||
|
|
||||||
When A VM running in a compute node is expected to route
|
|
||||||
packages to a router, a network namespace is expected to exist on
|
|
||||||
compute name that is named after the router ID
|
|
||||||
"""
|
|
||||||
router = self.server_stack.network_stack.gateway_details
|
|
||||||
self.assert_has_not_router_namespace(router=router)
|
|
||||||
|
|
||||||
@neutron.skip_unless_is_ovs()
|
L3_AGENT_MODE_DVR = re.compile(r'^dvr_')
|
||||||
def test_router_namespace_on_ovs(self):
|
|
||||||
"""Check router namespace is being created on compute host
|
|
||||||
|
|
||||||
When A VM running in a compute node is expected to route
|
|
||||||
packages to a router, a network namespace is expected to exist on
|
|
||||||
compute name that is named after the router ID
|
|
||||||
"""
|
|
||||||
router = self.server_stack.network_stack.gateway_details
|
|
||||||
self.assert_has_router_namespace(router=router)
|
|
||||||
|
|
||||||
@neutron.skip_unless_is_ovs()
|
def is_l3_agent_mode_dvr(agent_mode: str) -> bool:
|
||||||
@neutron.skip_if_missing_networking_extensions('dvr')
|
return L3_AGENT_MODE_DVR.match(agent_mode) is not None
|
||||||
def test_distributed_router_namespace(self):
|
|
||||||
"""Test that no router namespace is created for DVR on compute node
|
|
||||||
|
|
||||||
When A VM running in a compute node is not expected to route
|
|
||||||
packages to a given router, a network namespace is not expected to
|
|
||||||
exist on compute name that is named after the router ID
|
|
||||||
"""
|
|
||||||
router = self.distributed_router_stack.router_details
|
|
||||||
self.assertTrue(router['distributed'])
|
|
||||||
self.assert_has_not_router_namespace(router=router)
|
|
||||||
|
|
||||||
def assert_has_router_namespace(self, router: neutron.RouterType):
|
@neutron.skip_if_missing_networking_extensions('dvr')
|
||||||
router_namespace = f"qrouter-{router['id']}"
|
class DvrRouterNamespaceTest(RouterNamespaceTestBase, testtools.TestCase):
|
||||||
self.assertIn(router_namespace,
|
|
||||||
self.list_network_namespaces(),
|
|
||||||
"No such router network namespace on hypervisor host")
|
|
||||||
|
|
||||||
def assert_has_not_router_namespace(self, router: neutron.RouterType):
|
server_stack = tobiko.required_fixture(DvrServerStackFixture, setup=False)
|
||||||
router_namespace = f"qrouter-{router['id']}"
|
|
||||||
self.assertNotIn(router_namespace,
|
def setUp(self):
|
||||||
self.list_network_namespaces(),
|
super().setUp()
|
||||||
"Router network namespace found on hypervisor host")
|
if self.legacy_hostnames:
|
||||||
|
self.skipTest(f'Host(s) {self.legacy_hostnames!r} with legacy '
|
||||||
|
'L3 agent mode')
|
||||||
|
|
||||||
|
host_groups = ['compute']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hypervisor_ssh_client(self) -> ssh.SSHClientFixture:
|
def snat_namespace(self) -> str:
|
||||||
# Check the VM can reach a working gateway
|
return f'snat-{self.router_id}'
|
||||||
ping.assert_reachable_hosts([self.server_stack.ip_address])
|
|
||||||
# List namespaces on hypervisor node
|
|
||||||
hypervisor = topology.get_openstack_node(
|
|
||||||
hostname=self.server_stack.hypervisor_hostname)
|
|
||||||
return hypervisor.ssh_client
|
|
||||||
|
|
||||||
def list_network_namespaces(self) -> typing.List[str]:
|
_agent_modes: typing.Optional[typing.Dict[str, typing.List[str]]] = None
|
||||||
return ip.list_network_namespaces(
|
|
||||||
ssh_client=self.hypervisor_ssh_client)
|
@property
|
||||||
|
def agent_modes(self) \
|
||||||
|
-> typing.Dict[str, typing.List[str]]:
|
||||||
|
if self._agent_modes is None:
|
||||||
|
self._agent_modes = collections.defaultdict(list)
|
||||||
|
for node in topology.list_openstack_nodes(
|
||||||
|
hostnames=self.hostnames):
|
||||||
|
self._agent_modes[node.l3_agent_mode].append(node.name)
|
||||||
|
agent_modes_dump = json.dumps(self._agent_modes,
|
||||||
|
indent=4, sort_keys=True)
|
||||||
|
LOG.info(f"Got L3 agent modes:\n{agent_modes_dump}")
|
||||||
|
return self._agent_modes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def legacy_hostnames(self) -> typing.List[str]:
|
||||||
|
return self.agent_modes['legacy']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dvr_hostnames(self) -> typing.List[str]:
|
||||||
|
return self.agent_modes['dvr'] + self.agent_modes['dvr_no_external']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dvr_snat_hostnames(self) -> typing.List[str]:
|
||||||
|
return self.agent_modes['dvr_snat']
|
||||||
|
|
||||||
|
def test_1_dvr_router_without_server(self):
|
||||||
|
if self.dvr_hostnames:
|
||||||
|
self.cleanup_stacks()
|
||||||
|
self.setup_router()
|
||||||
|
topology.assert_namespace_not_in_hosts(
|
||||||
|
self.router_namespace,
|
||||||
|
self.snat_namespace,
|
||||||
|
hostnames=self.dvr_hostnames)
|
||||||
|
else:
|
||||||
|
self.skipTest(f'All hosts {self.hostnames!r} have '
|
||||||
|
'dvr_snat L3 agent mode')
|
||||||
|
|
||||||
|
def test_2_dvr_snat_router_namespaces(self):
|
||||||
|
if self.dvr_snat_hostnames:
|
||||||
|
self.setup_router()
|
||||||
|
topology.wait_for_namespace_in_hosts(
|
||||||
|
self.router_namespace,
|
||||||
|
self.snat_namespace,
|
||||||
|
hostnames=self.dvr_snat_hostnames)
|
||||||
|
else:
|
||||||
|
self.skipTest(f'Any host {self.hostnames!r} has '
|
||||||
|
'dvr_snat L3 agent mode')
|
||||||
|
|
||||||
|
def test_3_dvr_router_namespace_with_server(self):
|
||||||
|
self.setup_server()
|
||||||
|
self.wait_for_namespace_in_hypervisor_host()
|
||||||
|
|
||||||
|
def wait_for_namespace_in_hypervisor_host(self):
|
||||||
|
hypervisor_hostname = self.server_stack.hypervisor_hostname
|
||||||
|
agent_mode = topology.get_l3_agent_mode(hypervisor_hostname)
|
||||||
|
LOG.info(f"Hypervisor host '{hypervisor_hostname}' has DVR agent "
|
||||||
|
f"mode: '{agent_mode}'")
|
||||||
|
topology.wait_for_namespace_in_hosts(self.router_namespace,
|
||||||
|
hostnames=[hypervisor_hostname])
|
||||||
|
|
||||||
|
def test_4_server_is_reachable(self):
|
||||||
|
self.setup_server()
|
||||||
|
try:
|
||||||
|
self.server_stack.assert_is_reachable()
|
||||||
|
except ping.PingFailed:
|
||||||
|
server_id = self.server_stack.server_id
|
||||||
|
server_log = nova.get_console_output(server_id=server_id)
|
||||||
|
LOG.exception(f"Unable to reach server {server_id}...\n"
|
||||||
|
f"{server_log}\n")
|
||||||
|
self.wait_for_namespace_in_hypervisor_host()
|
||||||
|
nova.reboot_server(server=server_id)
|
||||||
|
self.server_stack.assert_is_reachable()
|
||||||
|
|
||||||
|
def cleanup_stacks(self):
|
||||||
|
for stack in [self.server_stack,
|
||||||
|
self.network_stack,
|
||||||
|
self.router_stack]:
|
||||||
|
tobiko.cleanup_fixture(stack)
|
||||||
|
|
||||||
|
def setup_router(self):
|
||||||
|
router = tobiko.setup_fixture(self.router_stack).router_details
|
||||||
|
router_dump = json.dumps(router, indent=4, sort_keys=True)
|
||||||
|
LOG.debug(f"Testing DVR router namespace: {router['id']}:\n"
|
||||||
|
f"{router_dump}\n")
|
||||||
|
self.assertTrue(router['distributed'])
|
||||||
|
|
||||||
|
def setup_network(self):
|
||||||
|
self.setup_router()
|
||||||
|
tobiko.setup_fixture(self.network_stack)
|
||||||
|
|
||||||
|
def setup_server(self):
|
||||||
|
self.setup_network()
|
||||||
|
tobiko.setup_fixture(self.server_stack)
|
||||||
|
Loading…
Reference in New Issue
Block a user