class FedoraServerStackFixture(_nova.CloudInitServerStackFixture): #: Glance image used to create a Nova server instance image_fixture = tobiko.required_fixture(FedoraImageFixture) #: Flavor used to create a Nova server instance flavor_stack = tobiko.required_fixture(FedoraFlavorStackFixture)
class UbuntuMinimalServerStackFixture(_nova.CloudInitServerStackFixture): #: Glance image used to create a Nova server instance image_fixture = tobiko.required_fixture(UbuntuMinimalImageFixture) #: Flavor used to create a Nova server instance flavor_stack = tobiko.required_fixture(UbuntuFlavorStackFixture)
class DVRTest(testtools.TestCase): router_stack = tobiko.required_fixture(NetworkWithNoServersStack) server_stack = tobiko.required_fixture( stacks.CirrosServerStackFixture) def setUp(self): super(DVRTest, self).setUp() if not self.router_stack.gateway_details.get('distributed'): tobiko.skip_test('No DVR enabled') def test_router_not_created_on_compute_if_no_instance_connected(self): '''Test that no router namespace is created for DVR on compute node Namespace should be only created if there is VM with router that is set as a default gateway. Need to verify that there will be no namespace created on the compute node where VM is connected to the external network. The same network is used as the default gateway for the router ''' router_namespace = f'qrouter-{self.router_stack.gateway_details["id"]}' cirros_hypervisor = topology.get_openstack_node( hostname=self.server_stack.hypervisor_host) namespaces = ip.list_network_namespaces( ssh_client=cirros_hypervisor.ssh_client) self.assertNotIn(router_namespace, namespaces)
class RedHatServerStackFixture(_centos.CentosServerStackFixture): #: Glance image used to create a Nova server instance # (alternative is given for cases the RHEL image is failed to be # set up) image_fixture = tobiko.required_fixture(RhelImageFixture) #: Flavor used to create a Nova server instance flavor_stack = tobiko.required_fixture(RedHatFlavorStackFixture)
class CentosServerStackFixture(_nova.CloudInitServerStackFixture): #: Glance image used to create a Nova server instance image_fixture = tobiko.required_fixture(CentosImageFixture) #: Flavor used to create a Nova server instance flavor_stack = tobiko.required_fixture(CentosFlavorStackFixture) # I expect CentOS based servers to be very slow to boot is_reachable_timeout = 900.
class ServerGroupTestCase(testtools.TestCase): affinity_stack = tobiko.required_fixture( stacks.AffinityServerGroupStackFixture) def test_affinity_server_group(self): group_id = self.affinity_stack.scheduler_group self.assertIsNotNone(group_id) anti_affinity_stack = tobiko.required_fixture( stacks.AntiAffinityServerGroupStackFixture) def test_anti_affinity_server_group(self): group_id = self.anti_affinity_stack.scheduler_group self.assertIsNotNone(group_id)
class OctaviaBasicTrafficScenarioTest(testtools.TestCase): """Octavia traffic scenario test. Create a load balancer with 2 members that run a server application, Create a client that is connected to the load balancer VIP port, Generate network traffic from the client to the load balanacer. """ loadbalancer_stack = tobiko.required_fixture( stacks.AmphoraIPv4LoadBalancerStack) listener_stack = tobiko.required_fixture( stacks.HttpRoundRobinAmphoraIpv4Listener) def setUp(self): # pylint: disable=no-member super(OctaviaBasicTrafficScenarioTest, self).setUp() # Wait for Octavia objects to be active LOG.info('Waiting for member ' f'{self.listener_stack.server_stack.stack_name} and ' f'for member ' f'{self.listener_stack.other_server_stack.stack_name} ' f'to be created...') self.listener_stack.wait_for_active_members() self.loadbalancer_stack.wait_for_octavia_service() self.listener_stack.wait_for_members_to_be_reachable() def test_round_robin_traffic(self): # For 5 minutes we ignore specific exceptions as we know # that Octavia resources are being provisioned for attempt in tobiko.retry(timeout=300.): try: octavia.check_members_balanced( pool_id=self.listener_stack.pool_id, ip_address=self.loadbalancer_stack.floating_ip_address, lb_algorithm=self.listener_stack.lb_algorithm, protocol=self.listener_stack.lb_protocol, port=self.listener_stack.lb_port) break except (octavia.RoundRobinException, octavia.TrafficTimeoutError, sh.ShellCommandFailed): LOG.exception(f"Traffic didn't reach all members after " f"#{attempt.number} attempts and " f"{attempt.elapsed_time} seconds") if attempt.is_last: raise
class OpenstackNodesTest(testtools.TestCase): topology = tobiko.required_fixture( topology.get_default_openstack_topology_class()) def test_public_ips(self): ips = dict() for node in self.topology.nodes: ping.ping(node.public_ip).assert_replied() other = ips.setdefault(node.public_ip, node) if node is not other: tobiko.fail(f"Nodes {node.name} and {other.name} have the " f"same IP: {node.public_ip}") def test_hostnames(self): hostnames = dict() for node in self.topology.nodes: hostname = sh.get_hostname(ssh_client=node.ssh_client) self.assertTrue(hostname.startswith(node.name)) other = hostnames.setdefault(hostname, node) if node is not other: tobiko.fail(f"Nodes {node.name} and {other.name} have the " f"same hostname: {hostname}") def test_network_namespaces(self): for node in self.topology.nodes: namespaces_ips = {} namespaces = ip.list_network_namespaces(ssh_client=node.ssh_client) for namespace in namespaces: ips = ip.list_ip_addresses(ssh_client=node.ssh_client, network_namespace=namespace) other_ips = namespaces_ips.setdefault(namespace, ips) if ips is not other_ips: tobiko.fail(f"Duplicate network namespace {namespace} in " f"node {node.name}: {other_ips}, {ips}")
class BackgroundProcessTest(testtools.TestCase): process = tobiko.required_fixture(MyBackgroundProcessFixture, setup=False) def test_start(self): self.stop_process() number0 = self.start_process() self.assertLess(number0, self.start_process(), "process has been restarted") def test_stop(self): number0 = self.start_process() self.stop_process() self.assertGreaterEqual(number0, self.start_process(), "process not stopped") def start_process(self) -> int: # pylint: disable=protected-access self.process.start() self.assertTrue(self.process.is_alive) self.assertTrue(os.path.isfile(self.process._pid_file)) return self.process.request_number() def stop_process(self): # pylint: disable=protected-access self.process.stop() self.assertFalse(self.process.is_alive) self.assertFalse(os.path.isfile(self.process._pid_file)) self.assertRaises(RuntimeError, self.process.request_number, timeout=5.)
class FloatingIpTest(testtools.TestCase): server = tobiko.required_fixture(stacks.CirrosServerStackFixture) def test_list_floating_ip(self): port_id = self.server.port_id floating_ips = neutron.list_floating_ips() floating_ip = floating_ips.with_items(port_id=port_id).unique self.assertEqual(floating_ip['floating_ip_address'], self.server.floating_ip_address) def test_list_floating_ip_with_port_id(self): port_id = self.server.port_id floating_ip = neutron.list_floating_ips(port_id=port_id).unique self.assertEqual(floating_ip['floating_ip_address'], self.server.floating_ip_address) def test_list_floating_ip_with_floating_ip_address(self): floating_ip_address = self.server.floating_ip_address floating_ip = neutron.list_floating_ips( floating_ip_address=floating_ip_address).unique self.assertEqual(floating_ip['port_id'], self.server.port_id) def test_find_floating_ip_with_port_id(self): port_id = self.server.port_id floating_ip = neutron.find_floating_ip(port_id=port_id, unique=True) self.assertEqual(floating_ip['floating_ip_address'], self.server.floating_ip_address)
class KeyPairTest(testtools.TestCase): stack = tobiko.required_fixture(stacks.KeyPairStackFixture) def test_key_files(self): self.assertTrue(os.path.isfile(self.stack.key_file)) self.assertTrue(os.path.isfile(self.stack.key_file + '.pub'))
class SSHShellConnectionTest(LocalShellConnectionTest): connection_class = sh.SSHShellConnection server = tobiko.required_fixture(stacks.UbuntuMinimalServerStackFixture) @property def ssh_client(self) -> ssh.SSHClientFixture: ssh_client = ssh.ssh_proxy_client() if isinstance(ssh_client, ssh.SSHClientFixture): return ssh_client nodes = topology.list_openstack_nodes() for node in nodes: if isinstance(node.ssh_client, ssh.SSHClientFixture): return node.ssh_client return self.server.ssh_client @property def is_local(self) -> bool: return False @property def hostname(self) -> str: return sh.get_hostname(ssh_client=self.ssh_client) @property def username(self) -> str: return self.ssh_client.username
class NetworkWithNetMtuWriteTest(NetworkTest): #: Stack of resources with a network with a gateway router stack = tobiko.required_fixture(stacks.NetworkWithNetMtuWriteStackFixture) def test_net_mtu_write(self): self.assertEqual(self.stack.mtu, self.stack.outputs.mtu)
class HeatStackFixtureTest(testtools.TestCase): stack = tobiko.required_fixture(MyStack) def test_get_stack(self): self.stack.wait_for_create_complete() stack = self.stack.get_stack() self.assertIsNotNone(stack) self.assertEqual(tobiko.get_fixture_name(MyStack), stack.stack_name) self.assertIsInstance(stack.id, str) self.assertIsInstance(stack.stack_status, str) def test_get_fixture_with_fixture_id_0(self): fixture_0 = tobiko.get_fixture(MyStack, fixture_id=0) self.assertIs(fixture_0, self.stack) def test_get_fixture_with_fixture_id_1(self): fixture_0 = tobiko.get_fixture(MyStack) fixture_1 = tobiko.get_fixture(MyStack, fixture_id=1) self.assertIsNot(fixture_0, fixture_1) stack_0 = tobiko.setup_fixture(fixture_0).get_stack() stack_1 = tobiko.setup_fixture(fixture_1).get_stack() self.assertNotEqual(stack_0.id, stack_1.id) self.assertEqual(tobiko.get_fixture_name(MyStack), stack_0.stack_name) self.assertEqual( tobiko.get_fixture_name(MyStack) + '-1', stack_1.stack_name)
class NetworkTest(testtools.TestCase): #: Resources stack with Nova server to send messages to stack = tobiko.required_fixture(stacks.CirrosPeerServerStackFixture) def test_stack_create_complete(self): self.stack.key_pair_stack.wait_for_create_complete() self.stack.network_stack.wait_for_create_complete() self.stack.peer_stack.wait_for_create_complete() self.stack.wait_for_create_complete() def test_ssh(self): """Test TCP connectivity to SSH server from VM to VM""" hostname = sh.ssh_hostname(ssh_client=self.stack.ssh_client) self.assertEqual(self.stack.server_name.lower(), hostname) def test_ping(self): """Test ICMP connectivity to from VM to VM""" ping.assert_reachable_hosts( [self.stack.ip_address], ssh_client=self.stack.peer_stack.ssh_client) # --- test l3_ha extension ------------------------------------------------ @neutron.skip_if_missing_networking_extensions('l3-ha') def test_l3_ha(self): """Test l3-ha network attribute""" gateway = self.stack.network_stack.gateway_details self.assertEqual(self.stack.network_stack.ha, gateway['ha'])
class NeutronNovaCommonReader(tobiko.SharedFixture): log_digger: files.MultihostLogFileDigger groups: typing.List[str] message_pattern: str datetime_pattern: typing.Pattern config = tobiko.required_fixture(_config.OpenStackTopologyConfig) service_name = neutron.SERVER def setup_fixture(self): self.datetime_pattern = re.compile( self.config.conf.log_datetime_pattern) self.log_digger = self.useFixture( _topology.get_log_file_digger( service_name=self.service_name, groups=self.groups, pattern=self.message_pattern)) self.read_responses() def _get_log_timestamp(self, log_line: str) -> float: found = self.datetime_pattern.match(log_line) if not found: return 0.0 return datetime.datetime.strptime( found.group(1), "%Y-%m-%d %H:%M:%S.%f").timestamp() def read_responses(self): raise NotImplementedError
class CirrosPingTest(PingTest): stack = tobiko.required_fixture(stacks.CirrosServerStackFixture) @property def ssh_client(self): return self.stack.ssh_client
class OpenvswitchTest(testtools.TestCase): stack = tobiko.required_fixture(stacks.CirrosServerStackFixture) def setUp(self): super(OpenvswitchTest, self).setUp() self.ovs_agents = neutron.list_agents(agent_type="Open vSwitch agent") self.router_id = self.stack.network_stack.gateway_id self.deleted_bridges = collections.defaultdict(set) def tearDown(self): super(OpenvswitchTest, self).tearDown() # Try to create all bridges which were deleted during the tests self._create_bridges() def _create_bridges(self): for host, bridges in self.deleted_bridges.items(): self._create_bridge(host, bridges) def _create_bridge(self, hostname, bridges): for br_name in bridges: agent_host = topology.get_openstack_node(hostname=hostname) sh.execute("sudo ovs-vsctl --may-exist add-br %s" % br_name, ssh_client=agent_host.ssh_client) def _delete_bridges(self, hostname, bridges): for br_name in bridges: agent_host = topology.get_openstack_node(hostname=hostname) sh.execute("sudo ovs-vsctl del-br %s" % br_name, ssh_client=agent_host.ssh_client) self.deleted_bridges[hostname].add(br_name) def _get_agent_from_host(self, hostname): host_shortname = tobiko.get_short_hostname(hostname) for agent in self.ovs_agents: if host_shortname == tobiko.get_short_hostname(agent['host']): return agent raise neutron.AgentNotFoundOnHost(agent_type="neutron-ovs-agent", host=hostname) @stacks.NetworkStackFixture.skip_if_router_is_distributed() @undercloud.skip_if_missing_undercloud def test_recreate_physical_bridge(self): # Check if vm is reachable before test ip_add = self.stack.ip_address ping.ping_until_received(ip_add).assert_replied() network_l3_agents = neutron.list_l3_agent_hosting_routers( self.router_id) for agent in network_l3_agents: # Get neutron-ovs-agent bridge mappings ovs_agent = self._get_agent_from_host(agent['host']) self._delete_bridges( agent['host'], ovs_agent['configurations']['bridge_mappings'].values()) ping.ping_until_unreceived(ip_add).assert_not_replied() self._create_bridges() ping.ping_until_received(ip_add).assert_replied()
class TcpSourceIpPortOvnIpv4Listener(HttpRoundRobinAmphoraIpv4Listener): loadbalancer = tobiko.required_fixture(OVNIPv4LoadBalancerStack) lb_protocol = 'TCP' lb_port = 22 has_monitor = False lb_algorithm = 'SOURCE_IP_PORT' pool_protocol = 'TCP' application_port = 22
class QosServerStackFixture(_ubuntu.UbuntuServerStackFixture): #: stack with the network with a qos policy network_stack = tobiko.required_fixture(QosNetworkStackFixture) @property def has_vlan(self) -> bool: # Trunk ports are not supported with QoS when ml2/ovs is used return False
class SSHProcessFixture(ProcessFixture): stack = tobiko.required_fixture( stacks.UbuntuMinimalServerStackFixture) def setup_fixture(self): self.ssh_client = self.stack.ssh_client super().setup_fixture()
class CirrosPsTest(LocalPsTest): is_cirros = True stack = tobiko.required_fixture(stacks.CirrosServerStackFixture) @property def ssh_client(self) -> ssh.SSHClientType: return self.stack.ssh_client
class AmphoraIPv4LoadBalancerStack(heat.HeatStackFixture): template = _hot.heat_template_file('octavia/load_balancer.yaml') vip_network = tobiko.required_fixture(_neutron.NetworkStackFixture) #: Floating IP network where the Neutron floating IP are created @property def floating_network(self) -> str: return self.vip_network.floating_network @property def has_floating_ip(self) -> bool: return bool(self.floating_network) ip_version = 4 provider = 'amphora' @property def vip_subnet_id(self): if self.ip_version == 4: return self.vip_network.ipv4_subnet_id else: return self.vip_network.ipv6_subnet_id def wait_for_active_loadbalancer(self, timeout: tobiko.Seconds = None): octavia.wait_for_status(status_key=octavia.PROVISIONING_STATUS, status=octavia.ACTIVE, get_client=octavia.get_loadbalancer, object_id=self.loadbalancer_id, timeout=timeout) def wait_for_update_loadbalancer(self, timeout: tobiko.Seconds = None): octavia.wait_for_status(status_key=octavia.PROVISIONING_STATUS, status=octavia.PENDING_UPDATE, get_client=octavia.get_loadbalancer, object_id=self.loadbalancer_id, timeout=timeout) def wait_for_octavia_service(self, interval: tobiko.Seconds = None, timeout: tobiko.Seconds = None, client=None): for attempt in tobiko.retry(timeout=timeout, interval=interval, default_timeout=180., default_interval=5.): try: octavia.list_amphorae(loadbalancer_id=self.loadbalancer_id, client=client) except octavia.OctaviaClientException as ex: LOG.debug(f"Error listing amphorae: {ex}") if attempt.is_last: raise LOG.info('Waiting for the LB to become functional again...') else: LOG.info('Octavia service is available!') break
class SSHClientManager(object): default = tobiko.required_fixture(_config.SSHDefaultConfigFixture) def __init__(self): self.clients = {} def get_client(self, host, hostname=None, username=None, port=None, proxy_jump=None, host_config=None, config_files=None, proxy_client=None, **connect_parameters) -> \ SSHClientFixture: if isinstance(host, netaddr.IPAddress): host = str(host) if host_config: hostname = hostname or host_config.hostname port = port or host_config.port username = username or host_config.username global_host_config = _config.ssh_host_config(host=host, config_files=config_files) hostname = hostname or global_host_config.hostname port = port or global_host_config.port username = username or global_host_config.username host_key = hostname, port, username, proxy_jump existing_client = self.clients.get(host_key) if isinstance(existing_client, SSHClientFixture): return existing_client # Put a placeholder to avoid infinite recursive lookup if existing_client is UNDEFINED_CLIENT: raise RuntimeError('Recursive SSH proxy client definition') self.clients[host_key] = UNDEFINED_CLIENT proxy_client = proxy_client or self.get_proxy_client( host=host, proxy_jump=proxy_jump, config_files=config_files) self.clients[host_key] = new_client = SSHClientFixture( host=host, hostname=hostname, port=port, username=username, proxy_client=proxy_client, host_config=host_config, **connect_parameters) return new_client def get_proxy_client(self, host=None, proxy_jump=None, host_config=None, config_files=None): if isinstance(proxy_jump, SSHClientFixture): return proxy_jump host_config = host_config or _config.ssh_host_config( host=host, config_files=config_files) proxy_jump = host_config.proxy_jump return proxy_jump and self.get_client(proxy_jump) or None
class Centos7ServerStackTest(CentosServerStackTest): #: Stack of resources with a server attached to a floating IP stack = tobiko.required_fixture(stacks.Centos7ServerStackFixture) def test_python(self): python_version = sh.execute(['python', '--version'], ssh_client=self.stack.ssh_client).stderr self.assertTrue(python_version.startswith('Python 2.'), python_version)
class ExtraDhcpOptsPortLoggingTest(testtools.TestCase): stack = tobiko.required_fixture(stacks.NetworkStackFixture) @pytest.mark.flaky(reruns=2, reruns_delay=60) def test_extra_dhcp_opts_logs_unsupported_options(self): # initialize logs that match the pattern topology.assert_ovn_unsupported_dhcp_option_messages() wrong_ipv4_option = 'wrong-ipv4-option' wrong_ipv6_option = 'bananas' a_valid_ipv4_option_used_for_ipv6 = 'log-server' extra_dhcp_opts = [{ 'opt_value': '1.1.1.1', 'opt_name': a_valid_ipv4_option_used_for_ipv6, 'ip_version': IPV6 }, { 'opt_value': 'ipv6.domain', 'opt_name': 'domain-search', 'ip_version': IPV6 }, { 'opt_value': '1600', 'opt_name': 'mtu', 'ip_version': IPV4 }, { 'opt_value': 'blablabla', 'opt_name': wrong_ipv4_option, 'ip_version': IPV4 }] # create port with extra-dhcp-opts port = neutron.create_port( **{ 'network_id': self.stack.network_id, 'extra_dhcp_opts': extra_dhcp_opts }) self.addCleanup(neutron.delete_port, port['id']) # find new logs that match the pattern invalid_options = [ wrong_ipv4_option, a_valid_ipv4_option_used_for_ipv6 ] # assert every invalid dhcp option is logged topology.assert_ovn_unsupported_dhcp_option_messages( unsupported_options=invalid_options, port_uuid=port['id']) extra_dhcp_opts.append({ 'opt_value': '1.1.1.1', 'opt_name': wrong_ipv6_option, 'ip_version': IPV6 }) # update port with new extra-dhcp-opts port = neutron.update_port(port['id'], **{'extra_dhcp_opts': extra_dhcp_opts}) invalid_options.append(wrong_ipv6_option) # assert every invalid dhcp option is logged topology.assert_ovn_unsupported_dhcp_option_messages( unsupported_options=invalid_options, port_uuid=port['id'])
class ProcessTest(testtools.TestCase): fixture = tobiko.required_fixture(ProcessFixture) def test_stdout(self): fixture = self.fixture sh.execute(f"echo some text > '{fixture.temp_filename}'", ssh_client=fixture.ssh_client) line = self.fixture.process.stdout.readline() self.assertEqual(b'some text\n', line)
class GetHostnameTest(testtools.TestCase): def test_hostname(self, expect_hostname: str = None, **execute_params): hostname = sh.get_hostname(**execute_params) self.assertIsInstance(hostname, str) if expect_hostname: self.assertEqual(expect_hostname, hostname) else: self.assertNotEqual('', hostname) def test_local_hostname(self): self.test_hostname(expect_hostname=socket.gethostname(), ssh_client=False) def test_ssh_hostname(self, ssh_client: ssh.SSHClientFixture = None): fixture = ssh.ssh_client_fixture(ssh_client) if fixture is None: expect_hostname = socket.gethostname() else: stdin, stdput, stderr = fixture.connect().exec_command('hostname') stdin.close() self.assertEqual(b'', stderr.read()) expect_hostname = stdput.read().decode().strip() self.test_hostname(ssh_client=ssh_client, expect_hostname=expect_hostname) def test_ssh_proxy_hostname(self): ssh_client = ssh.ssh_proxy_client() if ssh_client is None: tobiko.skip_test('SSH proxy server is not configured') self.test_ssh_hostname(ssh_client=ssh_client) cirros_server = tobiko.required_fixture(stacks.CirrosServerStackFixture) @keystone.skip_unless_has_keystone_credentials() def test_cirros_hostname(self): self.test_ssh_hostname(ssh_client=self.cirros_server.ssh_client) ubuntu_server = tobiko.required_fixture(stacks.UbuntuServerStackFixture) @keystone.skip_unless_has_keystone_credentials() def test_ubuntu_hostname(self): self.test_ssh_hostname(ssh_client=self.ubuntu_server.ssh_client)
class CirrosShellConnectionTest(SSHShellConnectionTest): connection_class = stacks.CirrosShellConnection server = tobiko.required_fixture(stacks.CirrosServerStackFixture) @property def ssh_client(self) -> ssh.SSHClientFixture: return self.server.ssh_client @property def is_cirros(self) -> bool: return True
class CirrosPeerServerStackTest(CirrosServerStackTest): #: Stack of resources with an HTTP server stack = tobiko.required_fixture(stacks.CirrosPeerServerStackFixture) @property def peer_ssh_client(self): return self.stack.peer_stack.ssh_client def test_ping_floating_ip(self): self.skipTest(f"Server '{self.stack.server_id}' has any floating IP")