diff --git a/tobiko/openstack/glance/_image.py b/tobiko/openstack/glance/_image.py index 6e12fa949..896bca414 100644 --- a/tobiko/openstack/glance/_image.py +++ b/tobiko/openstack/glance/_image.py @@ -18,7 +18,7 @@ import io import os import tempfile import time -import typing +import typing # noqa from oslo_log import log import requests @@ -27,6 +27,7 @@ import tobiko from tobiko.config import get_bool_env from tobiko.openstack.glance import _client from tobiko.openstack.glance import _io +from tobiko.openstack import keystone LOG = log.getLogger(__name__) @@ -79,15 +80,19 @@ class GlanceImageStatus(object): IMPORTING = u'importing' +@keystone.skip_unless_has_keystone_credentials() class GlanceImageFixture(_client.HasGlanceClientMixin, tobiko.SharedFixture): - image_name = None # type: str - username = None # type: str - password = None # type: str + image_name: typing.Optional[str] = None + username: typing.Optional[str] = None + password: typing.Optional[str] = None image = None wait_interval = 5. - def __init__(self, image_name=None, username=None, password=None): + def __init__(self, + image_name: typing.Optional[str] = None, + username: typing.Optional[str] = None, password: + typing.Optional[str] = None): super(GlanceImageFixture, self).__init__() if image_name: @@ -333,7 +338,7 @@ class FileGlanceImageFixture(UploadGranceImageFixture): class URLGlanceImageFixture(FileGlanceImageFixture): - image_url = None # type: str + image_url: typing.Optional[str] = None def __init__(self, image_url=None, **kwargs): super(URLGlanceImageFixture, self).__init__(**kwargs) diff --git a/tobiko/openstack/heat/_stack.py b/tobiko/openstack/heat/_stack.py index 2e6dc7c64..4d611585e 100644 --- a/tobiko/openstack/heat/_stack.py +++ b/tobiko/openstack/heat/_stack.py @@ -25,6 +25,7 @@ import tobiko from tobiko import config from tobiko.openstack.heat import _client from tobiko.openstack.heat import _template +from tobiko.openstack import keystone LOG = log.getLogger(__name__) @@ -58,6 +59,7 @@ def heat_stack_parameters(obj, stack=None): return parameters +@keystone.skip_unless_has_keystone_credentials() class HeatStackFixture(tobiko.SharedFixture): """Manages Heat stacks.""" diff --git a/tobiko/openstack/neutron/_extension.py b/tobiko/openstack/neutron/_extension.py index 433593177..f17779831 100644 --- a/tobiko/openstack/neutron/_extension.py +++ b/tobiko/openstack/neutron/_extension.py @@ -16,9 +16,11 @@ from __future__ import absolute_import import collections import tobiko +from tobiko.openstack import keystone from tobiko.openstack.neutron import _client +@keystone.skip_unless_has_keystone_credentials() class NetworkingExtensionsFixture(tobiko.SharedFixture): client = None diff --git a/tobiko/shell/ping/_interface.py b/tobiko/shell/ping/_interface.py index 7c09f21f5..029c32769 100644 --- a/tobiko/shell/ping/_interface.py +++ b/tobiko/shell/ping/_interface.py @@ -72,7 +72,9 @@ class PingInterfaceManager(tobiko.SharedFixture): usage = get_ping_usage(ssh_client) interface = find_ping_interface(usage=usage, interfaces=self.interfaces) - interface = interface or self.default_interface + if not interface: + LOG.error('Ping interface not found: using the default one') + interface = self.default_interface LOG.debug('Assign Ping interface %r to SSH client %r', interface, ssh_client) self.client_interfaces[ssh_client] = interface @@ -349,3 +351,38 @@ class InetToolsPingInterface(PingInterface): else: return ['-w', parameters.deadline, '-W', parameters.deadline] + + +IPUTILS_PING_USAGE = """ +ping: invalid option -- '-' +Usage: ping [-aAbBdDfhLnOqrRUvV] [-c count] [-i interval] [-I interface] + [-m mark] [-M pmtudisc_option] [-l preload] [-p pattern] [-Q tos] + [-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option] + [-w deadline] [-W timeout] [hop1 ...] destination +""".strip() + + +@ping_interface +class BsdPingInterface(PingInterface): + + def match_ping_usage(self, usage): + return usage.startswith(BSD_PING_USAGE) + + has_fragment_option = False + + def get_deadline_option(self, parameters): + return ['-t', parameters.deadline] + + +BSD_PING_USAGE = """ +ping: unrecognized option `--help' +usage: ping [-AaDdfnoQqRrv] [-c count] [-G sweepmaxsize] + [-g sweepminsize] [-h sweepincrsize] [-i wait] + [-l preload] [-M mask | time] [-m ttl] [-p pattern] + [-S src_addr] [-s packetsize] [-t timeout][-W waittime] + [-z tos] host + ping [-AaDdfLnoQqRrv] [-c count] [-I iface] [-i wait] + [-l preload] [-M mask | time] [-m ttl] [-p pattern] [-S src_addr] + [-s packetsize] [-T ttl] [-t timeout] [-W waittime] + [-z tos] mcast-group +""".strip() diff --git a/tobiko/shell/ping/_ping.py b/tobiko/shell/ping/_ping.py index d0cbfcc3d..76d9d71cf 100644 --- a/tobiko/shell/ping/_ping.py +++ b/tobiko/shell/ping/_ping.py @@ -292,35 +292,51 @@ def handle_ping_command_error(error): prefix = 'ping: ' if error.startswith('ping: '): text = error[len(prefix):] - - prefix = 'bad address ' - if text.startswith(prefix): - address = text[len(prefix):].replace("'", '').strip() - raise _exception.BadAddressPingError(address=address) - - prefix = 'local error: ' - if text.startswith(prefix): - details = text[len(prefix):].strip() - raise _exception.LocalPingError(details=details) - - prefix = 'sendto: ' - if text.startswith(prefix): - details = text[len(prefix):].strip() - raise _exception.SendToPingError(details=details) - - prefix = 'unknown host' - if text.startswith(prefix): - details = text[len(prefix):].strip() - raise _exception.UnknowHostError(details=details) - - suffix = ': Name or service not known' - if text.endswith(suffix): - details = text[:-len(suffix)].strip() - raise _exception.UnknowHostError(details=details) - - suffix = ': No route to host' - if text.endswith(suffix): - details = text[:-len(suffix)].strip() - raise _exception.UnknowHostError(details=details) - + handle_ping_bad_address_error(text) + handle_ping_local_error(text) + handle_ping_send_to_error(text) + handle_ping_unknow_host_error(text) raise _exception.PingError(details=text) + + +def handle_ping_bad_address_error(text): + prefix = 'bad address ' + if text.startswith(prefix): + address = text[len(prefix):].replace("'", '').strip() + raise _exception.BadAddressPingError(address=address) + + +def handle_ping_local_error(text): + prefix = 'local error: ' + if text.startswith(prefix): + details = text[len(prefix):].strip() + raise _exception.LocalPingError(details=details) + + +def handle_ping_send_to_error(text): + prefix = 'sendto: ' + if text.startswith(prefix): + details = text[len(prefix):].strip() + raise _exception.SendToPingError(details=details) + + +def handle_ping_unknow_host_error(text): + prefix = 'unknown host' + if text.startswith(prefix): + details = text[len(prefix):].strip() + raise _exception.UnknowHostError(details=details) + + suffix = ': Name or service not known' + if text.endswith(suffix): + details = text[:-len(suffix)].strip() + raise _exception.UnknowHostError(details=details) + + suffix = ': No route to host' + if text.endswith(suffix): + details = text[:-len(suffix)].strip() + raise _exception.UnknowHostError(details=details) + + suffix = ': Unknown host' + if text.endswith(suffix): + details = text[:-len(suffix)].strip().split()[-1] + raise _exception.UnknowHostError(details=details) diff --git a/tobiko/tests/functional/docker/test_client.py b/tobiko/tests/functional/docker/test_client.py index 2d334231e..168078f3b 100644 --- a/tobiko/tests/functional/docker/test_client.py +++ b/tobiko/tests/functional/docker/test_client.py @@ -21,6 +21,7 @@ import testtools import tobiko from tobiko import docker +from tobiko.openstack import keystone from tobiko.openstack import topology @@ -41,6 +42,7 @@ class DockerNodeFixture(tobiko.SharedFixture): ' '.join(node.name for node in nodes)) +@keystone.skip_unless_has_keystone_credentials() class DockerClientTest(testtools.TestCase): node = tobiko.required_setup_fixture(DockerNodeFixture) diff --git a/tobiko/tests/functional/openstack/stacks/test_centos.py b/tobiko/tests/functional/openstack/stacks/test_centos.py index be44acfbd..f6bbf95cd 100644 --- a/tobiko/tests/functional/openstack/stacks/test_centos.py +++ b/tobiko/tests/functional/openstack/stacks/test_centos.py @@ -20,11 +20,13 @@ import yaml import tobiko from tobiko.shell import sh +from tobiko.openstack import keystone from tobiko.openstack import nova from tobiko.openstack import stacks from tobiko.tests.functional.openstack.stacks import test_cirros +@keystone.skip_unless_has_keystone_credentials() class CentosServerStackTest(test_cirros.CirrosServerStackTest): """Tests connectivity to Nova instances via floating IPs""" diff --git a/tobiko/tests/functional/openstack/stacks/test_cirros.py b/tobiko/tests/functional/openstack/stacks/test_cirros.py index c7f679cb7..01093ed24 100644 --- a/tobiko/tests/functional/openstack/stacks/test_cirros.py +++ b/tobiko/tests/functional/openstack/stacks/test_cirros.py @@ -18,11 +18,13 @@ from __future__ import absolute_import import testtools import tobiko +from tobiko.openstack import keystone from tobiko.openstack import stacks from tobiko.shell import ping from tobiko.shell import sh +@keystone.skip_unless_has_keystone_credentials() class CirrosServerStackTest(testtools.TestCase): """Tests connectivity to Nova instances via floating IPs""" diff --git a/tobiko/tests/functional/openstack/stacks/test_neutron.py b/tobiko/tests/functional/openstack/stacks/test_neutron.py index 56408aab0..f5875c4e3 100644 --- a/tobiko/tests/functional/openstack/stacks/test_neutron.py +++ b/tobiko/tests/functional/openstack/stacks/test_neutron.py @@ -18,11 +18,13 @@ from __future__ import absolute_import import testtools import tobiko +from tobiko.openstack import keystone from tobiko.openstack import neutron from tobiko.openstack import stacks -class NetworkTestCase(testtools.TestCase): +@keystone.skip_unless_has_keystone_credentials() +class NetworkTest(testtools.TestCase): """Tests network creation""" #: Stack of resources with a network with a gateway router @@ -78,8 +80,9 @@ class NetworkTestCase(testtools.TestCase): self.stack.ipv6_gateway_addresses) +@keystone.skip_unless_has_keystone_credentials() @neutron.skip_if_missing_networking_extensions('net-mtu-write') -class NetworkWithNetMtuWriteTestCase(NetworkTestCase): +class NetworkWithNetMtuWriteTest(NetworkTest): #: Stack of resources with a network with a gateway router stack = tobiko.required_setup_fixture( @@ -89,15 +92,17 @@ class NetworkWithNetMtuWriteTestCase(NetworkTestCase): self.assertEqual(self.stack.mtu, self.stack.outputs.mtu) +@keystone.skip_unless_has_keystone_credentials() @neutron.skip_if_missing_networking_extensions('l3-ha') @neutron.skip_if_missing_networking_agents(binary='neutron-l3-agent', count=2) -class L3HaNetworkTestCase(NetworkTestCase): +class L3HaNetworkTest(NetworkTest): #: Stack of resources with a network with a gateway router stack = tobiko.required_setup_fixture(stacks.L3haNetworkStackFixture) +@keystone.skip_unless_has_keystone_credentials() class FloatingNetworkStackTest(testtools.TestCase): @stacks.skip_if_missing_floating_network diff --git a/tobiko/tests/functional/openstack/test_keystone.py b/tobiko/tests/functional/openstack/test_keystone.py index d60b4531f..24b20ba18 100644 --- a/tobiko/tests/functional/openstack/test_keystone.py +++ b/tobiko/tests/functional/openstack/test_keystone.py @@ -31,6 +31,7 @@ LOG = log.getLogger(__name__) CIENT_CLASSSES = v2_client.Client, v3_client.Client +@keystone.skip_unless_has_keystone_credentials() class TobikoKeystoneCredentialsCommandTest(testtools.TestCase): def test_execute(self): @@ -41,6 +42,7 @@ class TobikoKeystoneCredentialsCommandTest(testtools.TestCase): self.assertEqual(expected, actual) +@keystone.skip_unless_has_keystone_credentials() class KeystoneClientAPITest(testtools.TestCase): def test_get_keystone_client(self): diff --git a/tobiko/tests/functional/openstack/test_neutron.py b/tobiko/tests/functional/openstack/test_neutron.py index 8b3f9fe42..bb757fe55 100644 --- a/tobiko/tests/functional/openstack/test_neutron.py +++ b/tobiko/tests/functional/openstack/test_neutron.py @@ -20,6 +20,7 @@ import testtools import tobiko from tobiko import config +from tobiko.openstack import keystone from tobiko.openstack import neutron from tobiko.openstack import nova from tobiko.openstack import stacks @@ -29,6 +30,7 @@ from tobiko.openstack import tests CONF = config.CONF +@keystone.skip_unless_has_keystone_credentials() class NeutronApiTestCase(testtools.TestCase): """Tests network creation""" @@ -112,6 +114,7 @@ class NeutronApiTestCase(testtools.TestCase): self.assertIn(agent['id'], {a['id'] for a in agents}) +@keystone.skip_unless_has_keystone_credentials() class PortTest(testtools.TestCase): #: Stack of resources with a network with a gateway router @@ -159,6 +162,7 @@ class PortTest(testtools.TestCase): self.assertIn(port_address, cidr) +@keystone.skip_unless_has_keystone_credentials() class AgentTest(testtools.TestCase): def test_skip_if_missing_agents(self, count=1, should_skip=False, @@ -192,9 +196,6 @@ class AgentTest(testtools.TestCase): self.test_skip_if_missing_agents(count=1000000, should_skip=True) - -class NeutronAgentTest(testtools.TestCase): - def test_neutron_agents_are_alive(self): agents = tests.test_neutron_agents_are_alive() # check has agents and they are all alive diff --git a/tobiko/tests/functional/openstack/test_nova.py b/tobiko/tests/functional/openstack/test_nova.py index f71109cde..9dfb0b0fa 100644 --- a/tobiko/tests/functional/openstack/test_nova.py +++ b/tobiko/tests/functional/openstack/test_nova.py @@ -21,6 +21,7 @@ import netaddr import testtools import tobiko +from tobiko.openstack import keystone from tobiko.openstack import nova from tobiko.openstack import stacks @@ -34,6 +35,7 @@ class KeyPairTest(testtools.TestCase): self.assertTrue(os.path.isfile(self.stack.key_file + '.pub')) +@keystone.skip_unless_has_keystone_credentials() class ClientTest(testtools.TestCase): #: Stack of resources with a server attached to a floating IP @@ -117,6 +119,7 @@ class ClientTest(testtools.TestCase): self.assertEqual('ACTIVE', server.status) +@keystone.skip_unless_has_keystone_credentials() class HypervisorTest(testtools.TestCase): def test_skip_if_missing_hypervisors(self, count=1, should_skip=False, @@ -152,6 +155,7 @@ class HypervisorTest(testtools.TestCase): should_skip=True) +@keystone.skip_unless_has_keystone_credentials() class ServiceTest(testtools.TestCase): def test_wait_for_services_up(self): diff --git a/tobiko/tests/functional/openstack/test_octavia.py b/tobiko/tests/functional/openstack/test_octavia.py index 4aad8ad78..be0b5f58c 100644 --- a/tobiko/tests/functional/openstack/test_octavia.py +++ b/tobiko/tests/functional/openstack/test_octavia.py @@ -21,6 +21,7 @@ from tobiko.openstack import keystone from tobiko.openstack import octavia +@keystone.skip_unless_has_keystone_credentials() @keystone.skip_if_missing_service(name='octavia') class OctaviaClientAPITest(testtools.TestCase): diff --git a/tobiko/tests/functional/openstack/test_topology.py b/tobiko/tests/functional/openstack/test_topology.py index caa772c29..93407156c 100644 --- a/tobiko/tests/functional/openstack/test_topology.py +++ b/tobiko/tests/functional/openstack/test_topology.py @@ -18,12 +18,14 @@ from __future__ import absolute_import import testtools import tobiko +from tobiko.openstack import keystone from tobiko.openstack import nova from tobiko.openstack import topology from tobiko.shell import ping from tobiko.shell import sh +@keystone.skip_unless_has_keystone_credentials() class OpenStackTopologyTest(testtools.TestCase): topology = tobiko.required_setup_fixture(topology.OpenStackTopology) diff --git a/tobiko/tests/functional/openstack/tests/test_nova.py b/tobiko/tests/functional/openstack/tests/test_nova.py index 0d79dac93..ef7339380 100644 --- a/tobiko/tests/functional/openstack/tests/test_nova.py +++ b/tobiko/tests/functional/openstack/tests/test_nova.py @@ -19,6 +19,7 @@ import testtools import tobiko from tobiko.openstack import glance +from tobiko.openstack import keystone from tobiko.openstack import nova from tobiko.openstack import tests @@ -27,6 +28,7 @@ class MyServerStack(tests.TestServerCreationStack): pass +@keystone.skip_unless_has_keystone_credentials() class ServerCreationTest(testtools.TestCase): def test_server_creation(self): diff --git a/tobiko/tests/functional/podman/test_client.py b/tobiko/tests/functional/podman/test_client.py index 01df125d0..38dab57cf 100644 --- a/tobiko/tests/functional/podman/test_client.py +++ b/tobiko/tests/functional/podman/test_client.py @@ -21,6 +21,7 @@ import testtools import tobiko from tobiko import podman +from tobiko.openstack import keystone from tobiko.openstack import topology @@ -41,6 +42,7 @@ class PodmanNodeFixture(tobiko.SharedFixture): ' '.join(node.name for node in nodes)) +@keystone.skip_unless_has_keystone_credentials() class PodmanClientTest(testtools.TestCase): node = tobiko.required_setup_fixture(PodmanNodeFixture) diff --git a/tobiko/tests/functional/shell/fixtures.py b/tobiko/tests/functional/shell/fixtures.py index 7003158d2..957cf6d39 100644 --- a/tobiko/tests/functional/shell/fixtures.py +++ b/tobiko/tests/functional/shell/fixtures.py @@ -1,10 +1,12 @@ from __future__ import absolute_import import tobiko +from tobiko.openstack import keystone from tobiko.openstack import topology from tobiko.shell import ip +@keystone.skip_unless_has_keystone_credentials() class NetworkNamespaceFixture(tobiko.SharedFixture): network_namespace = None diff --git a/tobiko/tests/functional/shell/test_hostname.py b/tobiko/tests/functional/shell/test_hostname.py index 4fe4d4e07..8fa38d9db 100644 --- a/tobiko/tests/functional/shell/test_hostname.py +++ b/tobiko/tests/functional/shell/test_hostname.py @@ -23,9 +23,11 @@ import testtools import tobiko from tobiko.shell import sh from tobiko.shell import ssh +from tobiko.openstack import keystone from tobiko.openstack import stacks +@keystone.skip_unless_has_keystone_credentials() class HostnameTest(testtools.TestCase): server_stack = tobiko.required_setup_fixture( diff --git a/tobiko/tests/functional/shell/test_ifconfig.py b/tobiko/tests/functional/shell/test_ifconfig.py index 819531b08..dce8f38c8 100644 --- a/tobiko/tests/functional/shell/test_ifconfig.py +++ b/tobiko/tests/functional/shell/test_ifconfig.py @@ -22,6 +22,7 @@ import tobiko from tobiko.shell import ifconfig from tobiko.shell import sh from tobiko.shell import ssh +from tobiko.openstack import keystone from tobiko.openstack import stacks @@ -53,9 +54,11 @@ class IfconfigTest(testtools.TestCase): def test_list_ip_addresses_with_ipv6(self): self.test_list_ip_addresses(ip_version=6) + @keystone.skip_unless_has_keystone_credentials() def test_list_ip_addresses_with_cirros_server(self): self.test_list_ip_addresses(ssh_client=self.cirros_stack.ssh_client) + @keystone.skip_unless_has_keystone_credentials() def test_list_ip_addresses_with_ubuntu_server(self): self.test_list_ip_addresses(ssh_client=self.ubuntu_stack.ssh_client) diff --git a/tobiko/tests/functional/shell/test_ip.py b/tobiko/tests/functional/shell/test_ip.py index 0e960fe00..6ecb64412 100644 --- a/tobiko/tests/functional/shell/test_ip.py +++ b/tobiko/tests/functional/shell/test_ip.py @@ -15,8 +15,9 @@ # under the License. from __future__ import absolute_import +import os + import netaddr -import six import testtools import tobiko @@ -41,6 +42,8 @@ class IpTest(testtools.TestCase): def test_list_ip_addresses(self, ip_version=None, scope=None, **execute_params): + if not os.path.isfile('/bin/ip'): + self.skip("'bin/ip' command not found") ips = ip.list_ip_addresses(ip_version=ip_version, scope=scope, **execute_params) self.assertIsInstance(ips, tobiko.Selection) @@ -125,10 +128,12 @@ class IpTest(testtools.TestCase): **execute_params) def test_list_namespaces(self, **execute_params): + if not os.path.isfile('/bin/ip'): + self.skip("'bin/ip' command not found") namespaces = ip.list_network_namespaces(**execute_params) self.assertIsInstance(namespaces, list) for namespace in namespaces: - self.assertIsInstance(namespace, six.string_types) + self.assertIsInstance(namespace, str) self.test_list_ip_addresses(network_namespace=namespace) def test_list_namespaces_with_centos_server(self): diff --git a/tobiko/tests/functional/shell/test_ping.py b/tobiko/tests/functional/shell/test_ping.py index e9a2855d0..ecac7e7ac 100644 --- a/tobiko/tests/functional/shell/test_ping.py +++ b/tobiko/tests/functional/shell/test_ping.py @@ -15,6 +15,8 @@ # under the License. from __future__ import absolute_import +import os + import netaddr import testtools @@ -98,6 +100,8 @@ class PingTest(testtools.TestCase): def test_ping_hosts(self, ssh_client=None, network_namespace=None, **params): + if not os.path.isfile('/sbin/ip'): + self.skip("'/sbin/ip' command not found") ips = ip.list_ip_addresses(ssh_client=ssh_client, network_namespace=network_namespace) reachable_ips, unrecheable_ips = ping.ping_hosts( diff --git a/tools/common.py b/tools/common.py index 3bf3de638..f87f73488 100644 --- a/tools/common.py +++ b/tools/common.py @@ -51,18 +51,20 @@ def normalize_path(path): def execute(command, *args, **kwargs): capture_stdout = kwargs.pop('capture_stdout', True) universal_newlines = kwargs.pop('universal_newlines', True) + check = kwargs.pop('check', True) if args or kwargs: command = command.format(*args, **kwargs) command = command.strip() - if capture_stdout: - execute_func = subprocess.check_output - else: - execute_func = subprocess.check_call + stdout = capture_stdout and subprocess.PIPE or None - return execute_func(['/bin/bash', '-x', '-c', command], - shell=False, universal_newlines=universal_newlines) + result = subprocess.run(['/bin/bash', '-x', '-c', command], + stdout=stdout, shell=False, + universal_newlines=universal_newlines) + if check: + result.check_returncode() + return result.stdout def get_posargs(args=None): diff --git a/tools/run_tests.py b/tools/run_tests.py index 3eca5f903..67f57002b 100755 --- a/tools/run_tests.py +++ b/tools/run_tests.py @@ -106,7 +106,8 @@ def log_environ(): def log_tests_results(): common.execute('stestr last --all-attachments >> "{log_file}"', log_file=TOX_REPORT_LOG, - capture_stdout=False) + capture_stdout=False, + check=False) def run_test_cases(): diff --git a/tox.ini b/tox.ini index 25e63c52c..fa1cf35ea 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] -envlist = bindep,linters,py3,lower-constraints +envlist = bindep,linters,py3,lower-constraints,functional minversion = 3.8.0