Merge "Make bgpvpn-bagpipe tempest job voting again."
diff --git a/.zuul.yaml b/.zuul.yaml
index 4326959..a2c317f 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -110,7 +110,7 @@
QUOTAS:
quota_router: 100
quota_floatingip: 500
- quota_security_group: 100
+ quota_security_group: 150
quota_security_group_rule: 1000
# NOTE(slaweq): We can get rid of this hardcoded absolute path when
# devstack-tempest job will be switched to use lib/neutron instead of
diff --git a/neutron_tempest_plugin/api/test_security_groups.py b/neutron_tempest_plugin/api/test_security_groups.py
index c2e63da..67925f7 100644
--- a/neutron_tempest_plugin/api/test_security_groups.py
+++ b/neutron_tempest_plugin/api/test_security_groups.py
@@ -76,6 +76,39 @@
self.assertIn(
security_group_rule['id'], observerd_security_group_rules_ids)
+ @decorators.idempotent_id('b5923b1a-4d33-44e1-af25-088dcb55b02b')
+ def test_list_security_group_rules_contains_all_rules(self):
+ """Test list security group rules.
+
+ This test checks if all SG rules which belongs to the tenant OR
+ which belongs to the tenant's security group are listed.
+ """
+ security_group = self.create_security_group()
+ protocol = random.choice(list(base_security_groups.V4_PROTOCOL_NAMES))
+ security_group_rule = self.create_security_group_rule(
+ security_group=security_group,
+ project={'id': self.admin_client.tenant_id},
+ client=self.admin_client,
+ protocol=protocol,
+ direction=constants.INGRESS_DIRECTION)
+
+ # Create also other SG with some custom rule to check that regular user
+ # can't see this rule
+ admin_security_group = self.create_security_group(
+ project={'id': self.admin_client.tenant_id},
+ client=self.admin_client)
+ admin_security_group_rule = self.create_security_group_rule(
+ security_group=admin_security_group,
+ project={'id': self.admin_client.tenant_id},
+ client=self.admin_client,
+ protocol=protocol,
+ direction=constants.INGRESS_DIRECTION)
+
+ rules = self.client.list_security_group_rules()['security_group_rules']
+ rules_ids = [rule['id'] for rule in rules]
+ self.assertIn(security_group_rule['id'], rules_ids)
+ self.assertNotIn(admin_security_group_rule['id'], rules_ids)
+
@decorators.idempotent_id('7c0ecb10-b2db-11e6-9b14-000c29248b0d')
def test_create_bulk_sec_groups(self):
# Creates 2 sec-groups in one request
diff --git a/neutron_tempest_plugin/common/utils.py b/neutron_tempest_plugin/common/utils.py
index 631f75b..c8ff194 100644
--- a/neutron_tempest_plugin/common/utils.py
+++ b/neutron_tempest_plugin/common/utils.py
@@ -26,6 +26,7 @@
from urllib import parse as urlparse
import eventlet
+from tempest.lib import exceptions
SCHEMA_PORT_MAPPING = {
"http": 80,
@@ -106,3 +107,22 @@
if scheme in SCHEMA_PORT_MAPPING and not port:
netloc = netloc + ":" + str(SCHEMA_PORT_MAPPING[scheme])
return urlparse.urlunparse((scheme, netloc, url, params, query, fragment))
+
+
+def kill_nc_process(ssh_client):
+ cmd = "killall -q nc"
+ try:
+ ssh_client.exec_command(cmd)
+ except exceptions.SSHExecCommandFailed:
+ pass
+
+
+def spawn_http_server(ssh_client, port, message):
+ cmd = ("(echo -e 'HTTP/1.1 200 OK\r\n'; echo '%(msg)s') "
+ "| sudo nc -lp %(port)d &" % {'msg': message, 'port': port})
+ ssh_client.exec_command(cmd)
+
+
+def call_url_remote(ssh_client, url):
+ cmd = "curl %s --retry 3 --connect-timeout 2" % url
+ return ssh_client.exec_command(cmd)
diff --git a/neutron_tempest_plugin/scenario/base.py b/neutron_tempest_plugin/scenario/base.py
index 42bd33b..7b66494 100644
--- a/neutron_tempest_plugin/scenario/base.py
+++ b/neutron_tempest_plugin/scenario/base.py
@@ -12,6 +12,8 @@
# 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 distutils
+import re
import subprocess
from debtcollector import removals
@@ -30,6 +32,7 @@
from neutron_tempest_plugin.common import shell
from neutron_tempest_plugin.common import ssh
from neutron_tempest_plugin import config
+from neutron_tempest_plugin import exceptions
from neutron_tempest_plugin.scenario import constants
CONF = config.CONF
@@ -37,6 +40,45 @@
LOG = log.getLogger(__name__)
+def get_ncat_version(ssh_client=None):
+ cmd = "ncat --version 2>&1"
+ try:
+ version_result = shell.execute(cmd, ssh_client=ssh_client).stdout
+ except exceptions.ShellCommandFailed:
+ m = None
+ else:
+ m = re.match(r"Ncat: Version ([\d.]+) *.", version_result)
+ # NOTE(slaweq): by default lets assume we have ncat 7.60 which is in Ubuntu
+ # 18.04 which is used on u/s gates
+ return distutils.version.StrictVersion(m.group(1) if m else '7.60')
+
+
+def get_ncat_server_cmd(port, protocol, msg):
+ udp = ''
+ if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
+ udp = '-u'
+ cmd = "nc %(udp)s -p %(port)s -lk " % {
+ 'udp': udp, 'port': port}
+ if CONF.neutron_plugin_options.default_image_is_advanced:
+ cmd += "-c 'echo %s' &" % msg
+ else:
+ cmd += "-e echo %s &" % msg
+ return cmd
+
+
+def get_ncat_client_cmd(ip_address, port, protocol):
+ udp = ''
+ if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
+ udp = '-u'
+ cmd = 'echo "knock knock" | nc '
+ ncat_version = get_ncat_version()
+ if ncat_version > distutils.version.StrictVersion('7.60'):
+ cmd += '-z '
+ cmd += '-w 1 %(udp)s %(host)s %(port)s' % {
+ 'udp': udp, 'host': ip_address, 'port': port}
+ return cmd
+
+
class BaseTempestTestCase(base_api.BaseNetworkTest):
def create_server(self, flavor_ref, image_ref, key_name, networks,
@@ -426,13 +468,10 @@
Listener is created always on remote host.
"""
- udp = ''
- if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
- udp = '-u'
- cmd = "sudo nc %(udp)s -p %(port)s -lk -c echo %(msg)s &" % {
- 'udp': udp, 'port': port, 'msg': echo_msg}
try:
- return ssh_client.exec_command(cmd)
+ return ssh_client.execute_script(
+ get_ncat_server_cmd(port, protocol, echo_msg),
+ become_root=True)
except lib_exc.SSHTimeout as ssh_e:
LOG.debug(ssh_e)
self._log_console_output([server])
@@ -443,11 +482,7 @@
Client is always executed locally on host where tests are executed.
"""
- udp = ''
- if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
- udp = '-u'
- cmd = 'echo "knock knock" | nc -w 1 %(udp)s %(host)s %(port)s' % {
- 'udp': udp, 'host': ip_address, 'port': port}
+ cmd = get_ncat_client_cmd(ip_address, port, protocol)
result = shell.execute_local_command(cmd)
self.assertEqual(0, result.exit_status)
return result.stdout
diff --git a/neutron_tempest_plugin/scenario/test_port_forwardings.py b/neutron_tempest_plugin/scenario/test_port_forwardings.py
index 7283887..2d77b65 100644
--- a/neutron_tempest_plugin/scenario/test_port_forwardings.py
+++ b/neutron_tempest_plugin/scenario/test_port_forwardings.py
@@ -14,12 +14,12 @@
# under the License.
from neutron_lib import constants
-from neutron_lib.utils import test
from oslo_log import log
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from neutron_tempest_plugin.common import ssh
+from neutron_tempest_plugin.common import utils
from neutron_tempest_plugin import config
from neutron_tempest_plugin.scenario import base
@@ -81,27 +81,32 @@
return servers
def _test_udp_port_forwarding(self, servers):
+
+ def _message_received(server, ssh_client, expected_msg):
+ self.nc_listen(server,
+ ssh_client,
+ server['port_forwarding_udp']['internal_port'],
+ constants.PROTO_NAME_UDP,
+ expected_msg)
+ received_msg = self.nc_client(
+ self.fip['floating_ip_address'],
+ server['port_forwarding_udp']['external_port'],
+ constants.PROTO_NAME_UDP)
+ return expected_msg in received_msg
+
for server in servers:
- msg = "%s-UDP-test" % server['name']
+ expected_msg = "%s-UDP-test" % server['name']
ssh_client = ssh.Client(
self.fip['floating_ip_address'],
CONF.validation.image_ssh_user,
pkey=self.keypair['private_key'],
port=server['port_forwarding_tcp']['external_port'])
- self.nc_listen(server,
- ssh_client,
- server['port_forwarding_udp']['internal_port'],
- constants.PROTO_NAME_UDP,
- msg)
- for server in servers:
- expected_msg = "%s-UDP-test" % server['name']
- self.assertIn(
- expected_msg, self.nc_client(
- self.fip['floating_ip_address'],
- server['port_forwarding_udp']['external_port'],
- constants.PROTO_NAME_UDP))
+ utils.wait_until_true(
+ lambda: _message_received(server, ssh_client, expected_msg),
+ exception=RuntimeError(
+ "Timed out waiting for message from server {!r} ".format(
+ server['id'])))
- @test.unstable_test("bug 1850800")
@decorators.idempotent_id('ab40fc48-ca8d-41a0-b2a3-f6679c847bfe')
def test_port_forwarding_to_2_servers(self):
udp_sg_rule = {'protocol': constants.PROTO_NAME_UDP,
diff --git a/neutron_tempest_plugin/scenario/test_qos.py b/neutron_tempest_plugin/scenario/test_qos.py
index ba8cc88..f8f1b03 100644
--- a/neutron_tempest_plugin/scenario/test_qos.py
+++ b/neutron_tempest_plugin/scenario/test_qos.py
@@ -20,7 +20,6 @@
from oslo_log import log as logging
from tempest.common import utils as tutils
from tempest.lib import decorators
-from tempest.lib import exceptions
from neutron_tempest_plugin.api import base as base_api
from neutron_tempest_plugin.common import ssh
@@ -92,16 +91,8 @@
raise sc_exceptions.FileCreationFailedException(
file=self.FILE_PATH)
- @staticmethod
- def _kill_nc_process(ssh_client):
- cmd = "killall -q nc"
- try:
- ssh_client.exec_command(cmd, timeout=5)
- except exceptions.SSHExecCommandFailed:
- pass
-
def _check_bw(self, ssh_client, host, port, expected_bw=LIMIT_BYTES_SEC):
- self._kill_nc_process(ssh_client)
+ utils.kill_nc_process(ssh_client)
cmd = ("(nc -ll -p %(port)d < %(file_path)s > /dev/null &)" % {
'port': port, 'file_path': self.FILE_PATH})
ssh_client.exec_command(cmd, timeout=5)
@@ -130,7 +121,7 @@
except socket.timeout:
LOG.warning('Socket timeout while reading the remote file, bytes '
'read: %s', total_bytes_read)
- self._kill_nc_process(ssh_client)
+ utils.kill_nc_process(ssh_client)
return False
finally:
client_socket.close()
diff --git a/neutron_tempest_plugin/scenario/test_security_groups.py b/neutron_tempest_plugin/scenario/test_security_groups.py
index 7b43a7e..67d9306 100644
--- a/neutron_tempest_plugin/scenario/test_security_groups.py
+++ b/neutron_tempest_plugin/scenario/test_security_groups.py
@@ -16,9 +16,11 @@
from tempest.common import waiters
from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from neutron_tempest_plugin.common import ssh
+from neutron_tempest_plugin.common import utils
from neutron_tempest_plugin import config
from neutron_tempest_plugin.scenario import base
from neutron_tempest_plugin.scenario import constants as const
@@ -30,6 +32,39 @@
credentials = ['primary', 'admin']
required_extensions = ['router', 'security-group']
+ def _verify_http_connection(self, ssh_client, ssh_server,
+ test_ip, test_port, should_pass=True):
+ """Verify if HTTP connection works using remote hosts.
+
+ :param ssh.Client ssh_client: The client host active SSH client.
+ :param ssh.Client ssh_server: The HTTP server host active SSH client.
+ :param string test_ip: IP address of HTTP server
+ :param string test_port: Port of HTTP server
+ :param bool should_pass: Wheter test should pass or not.
+
+ :return: if passed or not
+ :rtype: bool
+ """
+ utils.kill_nc_process(ssh_server)
+ url = 'http://%s:%d' % (test_ip, test_port)
+ utils.spawn_http_server(ssh_server, port=test_port, message='foo_ok')
+ try:
+ ret = utils.call_url_remote(ssh_client, url)
+ if should_pass:
+ self.assertIn('foo_ok', ret)
+ return
+ self.assertNotIn('foo_ok', ret)
+ except Exception as e:
+ if not should_pass:
+ return
+ raise e
+
+ @classmethod
+ def setup_credentials(cls):
+ super(NetworkSecGroupTest, cls).setup_credentials()
+ cls.project_id = cls.os_primary.credentials.tenant_id
+ cls.network_client = cls.os_admin.network_client
+
@classmethod
def resource_setup(cls):
super(NetworkSecGroupTest, cls).resource_setup()
@@ -40,6 +75,12 @@
cls.create_router_interface(router['id'], cls.subnet['id'])
cls.keypair = cls.create_keypair()
+ def setUp(self):
+ super(NetworkSecGroupTest, self).setUp()
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.network_client.reset_quotas, self.project_id)
+ self.network_client.update_quotas(self.project_id, security_group=-1)
+
def create_vm_testing_sec_grp(self, num_servers=2, security_groups=None,
ports=None):
"""Create instance for security group testing
@@ -293,3 +334,65 @@
self.check_connectivity(fip['floating_ip_address'],
CONF.validation.image_ssh_user,
self.keypair['private_key'])
+
+ @decorators.idempotent_id('f07d0159-8f9e-4faa-87f5-a869ab0ad489')
+ def test_multiple_ports_portrange_remote(self):
+ ssh_clients, fips, servers = self.create_vm_testing_sec_grp(
+ num_servers=3)
+ secgroups = []
+ ports = []
+
+ # Create remote and test security groups
+ for i in range(0, 2):
+ secgroups.append(
+ self.create_security_group(name='secgrp-%d' % i))
+ # configure sec groups to support SSH connectivity
+ self.create_loginable_secgroup_rule(
+ secgroup_id=secgroups[-1]['id'])
+
+ # Configure security groups, first two servers as remotes
+ for i, server in enumerate(servers):
+ port = self.client.list_ports(
+ network_id=self.network['id'], device_id=server['server'][
+ 'id'])['ports'][0]
+ ports.append(port)
+ secgroup = secgroups[0 if i in range(0, 2) else 1]
+ self.client.update_port(port['id'], security_groups=[
+ secgroup['id']])
+
+ # verify SSH functionality
+ for fip in fips:
+ self.check_connectivity(fip['floating_ip_address'],
+ CONF.validation.image_ssh_user,
+ self.keypair['private_key'])
+
+ test_ip = ports[2]['fixed_ips'][0]['ip_address']
+
+ # verify that conections are not working
+ for port in range(80, 84):
+ self._verify_http_connection(
+ ssh_clients[0],
+ ssh_clients[2],
+ test_ip, port,
+ should_pass=False)
+
+ # add two remote-group rules with port-ranges
+ rule_list = [{'protocol': constants.PROTO_NUM_TCP,
+ 'direction': constants.INGRESS_DIRECTION,
+ 'port_range_min': '80',
+ 'port_range_max': '81',
+ 'remote_group_id': secgroups[0]['id']},
+ {'protocol': constants.PROTO_NUM_TCP,
+ 'direction': constants.INGRESS_DIRECTION,
+ 'port_range_min': '82',
+ 'port_range_max': '83',
+ 'remote_group_id': secgroups[0]['id']}]
+ self.create_secgroup_rules(
+ rule_list, secgroup_id=secgroups[1]['id'])
+
+ # verify that conections are working
+ for port in range(80, 84):
+ self._verify_http_connection(
+ ssh_clients[0],
+ ssh_clients[2],
+ test_ip, port)
diff --git a/neutron_tempest_plugin/services/network/json/network_client.py b/neutron_tempest_plugin/services/network/json/network_client.py
index 521e2be..ddb6f95 100644
--- a/neutron_tempest_plugin/services/network/json/network_client.py
+++ b/neutron_tempest_plugin/services/network/json/network_client.py
@@ -893,6 +893,15 @@
self.expected_success(204, resp.status)
return service_client.ResponseBody(resp, body)
+ def list_security_group_rules(self, **kwargs):
+ uri = '%s/security-group-rules' % self.uri_prefix
+ if kwargs:
+ uri += '?' + urlparse.urlencode(kwargs, doseq=1)
+ resp, body = self.get(uri)
+ self.expected_success(200, resp.status)
+ body = jsonutils.loads(body)
+ return service_client.ResponseBody(resp, body)
+
def create_security_group_rule(self, direction, security_group_id,
**kwargs):
post_body = {'security_group_rule': kwargs}
diff --git a/releasenotes/notes/drop-py-2-7-74b8379cab4cdc5a.yaml b/releasenotes/notes/drop-py-2-7-74b8379cab4cdc5a.yaml
new file mode 100644
index 0000000..7d49171
--- /dev/null
+++ b/releasenotes/notes/drop-py-2-7-74b8379cab4cdc5a.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+ - |
+ Python 2.7 support has been dropped. Last release of neutron-tempest-plugin
+ to support py2.7 is OpenStack Train. The minimum version of Python now
+ supported by neutron-tempest-plugin is Python 3.6.
diff --git a/requirements.txt b/requirements.txt
index 9a5e99f..d3fa3eb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,7 +5,6 @@
pbr!=2.1.0,>=2.0.0 # Apache-2.0
neutron-lib>=1.25.0 # Apache-2.0
oslo.config>=5.2.0 # Apache-2.0
-ipaddress>=1.0.17;python_version<'3.3' # PSF
netaddr>=0.7.18 # BSD
os-ken>=0.3.0 # Apache-2.0
oslo.log>=3.36.0 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index ff12b10..1ac729c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,6 +6,7 @@
author = OpenStack
author-email = openstack-discuss@lists.openstack.org
home-page = https://opendev.org/openstack/neutron-tempest-plugin
+requires-python = >=3.6
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
@@ -13,8 +14,6 @@
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
- Programming Language :: Python :: 2
- Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
diff --git a/test-requirements.txt b/test-requirements.txt
index 8b251f6..905420c 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -7,8 +7,7 @@
coverage!=4.4,>=4.0 # Apache-2.0
flake8-import-order==0.12 # LGPLv3
python-subunit>=1.0.0 # Apache-2.0/BSD
-sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD
-sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2;python_version>='3.4' # BSD
+sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2 # BSD
oslotest>=3.2.0 # Apache-2.0
stestr>=1.0.0 # Apache-2.0
testtools>=2.2.0 # MIT
diff --git a/tox.ini b/tox.ini
index 95352a2..19e006a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,9 +1,11 @@
[tox]
-minversion = 2.0
+minversion = 3.1
envlist = pep8
skipsdist = True
+ignore_basepython_conflict = True
[testenv]
+basepython = python3
usedevelop = True
setenv =
VIRTUAL_ENV={envdir}
@@ -19,7 +21,6 @@
commands = stestr run --slowest {posargs}
[testenv:pep8]
-basepython = python3
commands =
sh ./tools/misc-sanity-checks.sh
flake8
@@ -27,11 +28,9 @@
sh
[testenv:venv]
-basepython = python3
commands = {posargs}
[testenv:cover]
-basepython = python3
setenv =
{[testenv]setenv}
PYTHON=coverage run --source neutron_tempest_plugin --parallel-mode
@@ -42,16 +41,13 @@
coverage xml -o cover/coverage.xml
[testenv:docs]
-basepython = python3
commands = python setup.py build_sphinx
[testenv:releasenotes]
-basepython = python3
commands =
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
[testenv:debug]
-basepython = python3
commands = oslo_debug_helper -t neutron_tempest_plugin/ {posargs}
[flake8]