Merge "Add support MTU tests in the same network type"
diff --git a/.zuul.yaml b/.zuul.yaml
index ba7e158..07bce4e 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -76,10 +76,10 @@
       devstack_services:
         cinder: False
         designate: True
-        q-dns: True
+        neutron-dns: True
         tempest: True
       tempest_test_regex: '^neutron_tempest_plugin\.scenario\.test_dns_integration'
-      tox_venvlist: all-plugin
+      tox_envlist: all-plugin
     irrelevant-files:
       - ^(test-|)requirements.txt$
       - ^releasenotes/.*$
diff --git a/HACKING.rst b/HACKING.rst
index 8c6d928..cd3c49c 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -1,4 +1,4 @@
 openstack Style Commandments
 ===============================================
 
-Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
+Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/
diff --git a/neutron_tempest_plugin/api/admin/test_logging.py b/neutron_tempest_plugin/api/admin/test_logging.py
new file mode 100644
index 0000000..f4cbe29
--- /dev/null
+++ b/neutron_tempest_plugin/api/admin/test_logging.py
@@ -0,0 +1,74 @@
+# Copyright 2017 Fujitsu Limited.
+#
+#    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.
+
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions
+import testscenarios
+
+from neutron_tempest_plugin.api import base
+
+load_tests = testscenarios.load_tests_apply_scenarios
+
+
+class LoggingTestJSON(base.BaseAdminNetworkTest):
+
+    required_extensions = ['logging', 'standard-attr-description']
+
+    @decorators.idempotent_id('8d2e1ba5-455b-4519-a88e-e587002faba6')
+    def test_log_lifecycle(self):
+        name = data_utils.rand_name('test-log')
+        description = data_utils.rand_name('test-log-desc')
+        log = self.create_log(name=name, description=description,
+                              resource_type='security_group', enabled=True)
+
+        # Test 'show log'
+        retrieved_log = self.admin_client.show_log(log['id'])['log']
+        self.assertEqual(name, retrieved_log['name'])
+        self.assertEqual(description, retrieved_log['description'])
+        self.assertEqual('security_group', retrieved_log['resource_type'])
+        self.assertTrue(retrieved_log['enabled'])
+
+        # Test 'list logs'
+        logs = self.admin_client.list_logs()['logs']
+        logs_ids = [log_object['id'] for log_object in logs]
+        self.assertIn(log['id'], logs_ids)
+
+        # Test 'update log'
+        update_description = data_utils.rand_name('test-log')
+        self.admin_client.update_log(log['id'],
+                                     description=update_description,
+                                     enabled=False)
+        retrieved_log = self.admin_client.show_log(log['id'])['log']
+        self.assertEqual(update_description, retrieved_log['description'])
+        self.assertFalse(retrieved_log['enabled'])
+
+        # Test 'delete log'
+        self.admin_client.delete_log(log['id'])
+        self.assertRaises(exceptions.NotFound,
+                          self.admin_client.show_log, log['id'])
+
+    @decorators.idempotent_id('1af6cdab-0eb0-4e13-8027-d89cf1c7a87a')
+    def test_list_supported_logging_types(self):
+        # List supported logging types
+        # Since returned logging types depends on loaded backend drivers
+        # this test is checking only if returned keys are same as expected keys
+        expected_log_keys = ['type']
+
+        log_types = self.admin_client.list_loggable_resources()
+        actual_list_log_types = log_types['loggable_resources']
+
+        # Verify that only required fields present in logging types
+        for log_type in actual_list_log_types:
+            self.assertEqual(tuple(expected_log_keys), tuple(log_type.keys()))
diff --git a/neutron_tempest_plugin/api/admin/test_logging_negative.py b/neutron_tempest_plugin/api/admin/test_logging_negative.py
new file mode 100644
index 0000000..b975cd6
--- /dev/null
+++ b/neutron_tempest_plugin/api/admin/test_logging_negative.py
@@ -0,0 +1,52 @@
+#    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.
+
+from oslo_utils import uuidutils
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+from neutron_tempest_plugin.api import base
+
+
+class LoggingNegativeTestJSON(base.BaseAdminNetworkTest):
+
+    required_extensions = ['logging', 'standard-attr-description']
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('5fc61e24-cad5-4d86-a2d4-f40c0fa0a54c')
+    def test_create_log_with_invalid_resource_type(self):
+        log_args = {'name': data_utils.rand_name('test-log'),
+                    'description': data_utils.rand_name('test-log-desc'),
+                    'resource_type': 'fake_resource'}
+        self.assertRaises(lib_exc.BadRequest,
+                          self.admin_client.create_log, **log_args)
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('7ed63170-0748-44b7-b0a0-64bfd9390dac')
+    def test_create_log_with_nonexistent_port(self):
+        log_args = {'name': data_utils.rand_name('test-log'),
+                    'description': data_utils.rand_name('test-log-desc'),
+                    'resource_type': 'security_group',
+                    'target_id': uuidutils.generate_uuid()}
+        self.assertRaises(lib_exc.NotFound,
+                          self.admin_client.create_log, **log_args)
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('89194c6b-8f47-400b-979b-072b1c1f767b')
+    def test_create_log_with_nonexistent_sg(self):
+        log_args = {'name': data_utils.rand_name('test-log'),
+                    'description': data_utils.rand_name('test-log-desc'),
+                    'resource_type': 'security_group',
+                    'resource_id': uuidutils.generate_uuid()}
+        self.assertRaises(lib_exc.NotFound,
+                          self.admin_client.create_log, **log_args)
diff --git a/neutron_tempest_plugin/api/base.py b/neutron_tempest_plugin/api/base.py
index 68b5680..7b333fe 100644
--- a/neutron_tempest_plugin/api/base.py
+++ b/neutron_tempest_plugin/api/base.py
@@ -121,6 +121,7 @@
         cls.admin_subnetpools = []
         cls.security_groups = []
         cls.projects = []
+        cls.log_objects = []
 
     @classmethod
     def resource_cleanup(cls):
@@ -213,6 +214,11 @@
                 cls._try_delete_resource(cls.admin_client.delete_qos_policy,
                                          qos_policy['id'])
 
+            # Clean up log_objects
+            for log_object in cls.log_objects:
+                cls._try_delete_resource(cls.admin_client.delete_log,
+                                         log_object['id'])
+
         super(BaseNetworkTest, cls).resource_cleanup()
 
     @classmethod
@@ -516,6 +522,23 @@
         return service_profile
 
     @classmethod
+    def create_log(cls, name, description=None,
+                   resource_type='security_group', resource_id=None,
+                   target_id=None, event='ALL', enabled=True):
+        """Wrapper utility that returns a test log object."""
+        log_args = {'name': name,
+                    'description': description,
+                    'resource_type': resource_type,
+                    'resource_id': resource_id,
+                    'target_id': target_id,
+                    'event': event,
+                    'enabled': enabled}
+        body = cls.admin_client.create_log(**log_args)
+        log_object = body['log']
+        cls.log_objects.append(log_object)
+        return log_object
+
+    @classmethod
     def get_unused_ip(cls, net_id, ip_version=None):
         """Get an unused ip address in a allocation pool of net"""
         body = cls.admin_client.list_ports(network_id=net_id)
diff --git a/neutron_tempest_plugin/api/base_security_groups.py b/neutron_tempest_plugin/api/base_security_groups.py
index cda18b8..127bbd9 100644
--- a/neutron_tempest_plugin/api/base_security_groups.py
+++ b/neutron_tempest_plugin/api/base_security_groups.py
@@ -19,17 +19,45 @@
 from neutron_tempest_plugin.api import base
 
 
-V4_PROTOCOL_NAMES = set(key for key in constants.IP_PROTOCOL_MAP if
-                        'v6' not in key)
-V4_PROTOCOL_INTS = set(v for k, v in constants.IP_PROTOCOL_MAP.items()
-                       if 'v6' not in k)
+# NOTE(yamamoto): The list of protocols here is what we had in Ocata.
+# (neutron-lib 1.1.0)
+# Why don't we just use neutron_lib.constants.IP_PROTOCOL_MAP etc here?
+# Tempest is branchless and thus supposed to work against older deployments.
+# Also, it's supposed to work against other implementations, which might not
+# support the same set as the reference implementation. Ideally SG can have
+# a way to discover the set of usable protocols. But for now, we need to be
+# conservative.
+
+V4_PROTOCOL_NAMES = {
+    'ah',
+    'dccp',
+    'egp',
+    'esp',
+    'gre',
+    'icmp',
+    'igmp',
+    'ospf',
+    'pgm',
+    'rsvp',
+    'sctp',
+    'tcp',
+    'udp',
+    'udplite',
+    'vrrp',
+}
+V4_PROTOCOL_INTS = set(v for k, v in constants.IP_PROTOCOL_MAP.items() if
+                       k in V4_PROTOCOL_NAMES)
 V6_PROTOCOL_LEGACY = set([constants.PROTO_NAME_IPV6_ICMP_LEGACY])
-V6_PROTOCOL_NAMES = (
-    set(key for key in constants.IP_PROTOCOL_MAP if 'v6' in key) -
-    V6_PROTOCOL_LEGACY
-)
+V6_PROTOCOL_NAMES = {
+    'ipv6-encap',
+    'ipv6-frag',
+    'ipv6-icmp',
+    'ipv6-nonxt',
+    'ipv6-opts',
+    'ipv6-route',
+}
 V6_PROTOCOL_INTS = set(v for k, v in constants.IP_PROTOCOL_MAP.items() if
-                       'v6' in k)
+                       k in (V6_PROTOCOL_NAMES | V6_PROTOCOL_LEGACY))
 
 
 class BaseSecGroupTest(base.BaseNetworkTest):
diff --git a/neutron_tempest_plugin/common/constants.py b/neutron_tempest_plugin/common/constants.py
index 4ad780d..4dc7844 100644
--- a/neutron_tempest_plugin/common/constants.py
+++ b/neutron_tempest_plugin/common/constants.py
@@ -123,10 +123,6 @@
 # agent has just returned to alive after being dead
 AGENT_REVIVED = 'revived'
 
-INGRESS_DIRECTION = 'ingress'
-EGRESS_DIRECTION = 'egress'
-
-VALID_DIRECTIONS = (INGRESS_DIRECTION, EGRESS_DIRECTION)
 VALID_ETHERTYPES = (lib_constants.IPv4, lib_constants.IPv6)
 
 IP_ALLOWED_VERSIONS = [lib_constants.IP_VERSION_4, lib_constants.IP_VERSION_6]
diff --git a/neutron_tempest_plugin/scenario/test_floatingip.py b/neutron_tempest_plugin/scenario/test_floatingip.py
index 0febce2..b253890 100644
--- a/neutron_tempest_plugin/scenario/test_floatingip.py
+++ b/neutron_tempest_plugin/scenario/test_floatingip.py
@@ -21,6 +21,7 @@
 import testscenarios
 from testscenarios.scenarios import multiply_scenarios
 
+from neutron_lib import constants as lib_constants
 from neutron_tempest_plugin.common import ssh
 from neutron_tempest_plugin.common import utils as common_utils
 from neutron_tempest_plugin import config
@@ -59,6 +60,17 @@
             cls._dest_network = cls._create_dest_network()
 
     @classmethod
+    def _get_external_gateway(cls):
+        if CONF.network.public_network_id:
+            subnets = cls.os_admin.network_client.list_subnets(
+                network_id=CONF.network.public_network_id)
+
+            for subnet in subnets['subnets']:
+                if (subnet['gateway_ip']
+                    and subnet['ip_version'] == lib_constants.IP_VERSION_4):
+                    return subnet['gateway_ip']
+
+    @classmethod
     def _create_dest_network(cls):
         network = cls.create_network()
         subnet = cls.create_subnet(network,
@@ -157,3 +169,30 @@
     @decorators.idempotent_id('f18f0090-3289-4783-b956-a0f8ac511e8b')
     def test_east_west(self):
         self._test_east_west()
+
+
+class DefaultSnatToExternal(FloatingIpTestCasesMixin,
+                            base.BaseTempestTestCase):
+    same_network = True
+
+    @decorators.idempotent_id('3d73ea1a-27c6-45a9-b0f8-04a283d9d764')
+    def test_snat_external_ip(self):
+        """Check connectivity to an external IP"""
+        gateway_external_ip = self._get_external_gateway()
+
+        if not gateway_external_ip:
+            raise self.skipTest("IPv4 gateway is not configured for public "
+                                "network or public_network_id is not "
+                                "configured")
+        proxy = self._create_server()
+        proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
+                                  CONF.validation.image_ssh_user,
+                                  pkey=self.keypair['private_key'])
+        src_server = self._create_server(create_floating_ip=False)
+        src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
+        ssh_client = ssh.Client(src_server_ip,
+                                CONF.validation.image_ssh_user,
+                                pkey=self.keypair['private_key'],
+                                proxy_client=proxy_client)
+        self.check_remote_connectivity(ssh_client,
+                                       gateway_external_ip)
diff --git a/neutron_tempest_plugin/services/network/json/network_client.py b/neutron_tempest_plugin/services/network/json/network_client.py
index 48b537d..a48db36 100644
--- a/neutron_tempest_plugin/services/network/json/network_client.py
+++ b/neutron_tempest_plugin/services/network/json/network_client.py
@@ -58,6 +58,8 @@
             'minimum_bandwidth_rules': 'qos',
             'rule_types': 'qos',
             'rbac-policies': '',
+            'logs': 'log',
+            'loggable_resources': 'log',
         }
         service_prefix = service_resource_prefix_map.get(
             plural_name)
diff --git a/playbooks/neutron-tempest-plugin-dvr-multinode-scenario/run.yaml b/playbooks/neutron-tempest-plugin-dvr-multinode-scenario/run.yaml
index 2102bb5..a9ce3e0 100644
--- a/playbooks/neutron-tempest-plugin-dvr-multinode-scenario/run.yaml
+++ b/playbooks/neutron-tempest-plugin-dvr-multinode-scenario/run.yaml
@@ -34,7 +34,6 @@
           export DEVSTACK_GATE_CONFIGDRIVE=0
           export DEVSTACK_GATE_TEMPEST_REGEX="(neutron_tempest_plugin.scenario)"
           export DEVSTACK_LOCAL_CONFIG="enable_plugin neutron-tempest-plugin git://git.openstack.org/openstack/neutron-tempest-plugin"
-          export TEMPEST_CONCURRENCY=2
           # Test DVR works multinode
           export DEVSTACK_GATE_NEUTRON_DVR=1
           export BRANCH_OVERRIDE=default
diff --git a/playbooks/neutron-tempest-plugin-scenario-linuxbridge/run.yaml b/playbooks/neutron-tempest-plugin-scenario-linuxbridge/run.yaml
index 65e8b12..02cdf83 100644
--- a/playbooks/neutron-tempest-plugin-scenario-linuxbridge/run.yaml
+++ b/playbooks/neutron-tempest-plugin-scenario-linuxbridge/run.yaml
@@ -53,7 +53,6 @@
               export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
           fi
 
-          export TEMPEST_CONCURRENCY=2
           export PROJECTS="openstack/neutron-tempest-plugin $PROJECTS"
           function gate_hook {
               bash -xe $BASE/new/neutron/neutron/tests/contrib/gate_hook.sh dsvm-scenario-linuxbridge dvrskip
diff --git a/requirements.txt b/requirements.txt
index e546885..77875ed 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,17 +2,17 @@
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
 
-pbr>=2.0 # Apache-2.0
-neutron-lib>=1.9.0 # Apache-2.0
-oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
+pbr!=2.1.0,>=2.0.0 # Apache-2.0
+neutron-lib>=1.13.0 # Apache-2.0
+oslo.config>=5.1.0 # Apache-2.0
 ipaddress>=1.0.16;python_version<'3.3' # PSF
-netaddr!=0.7.16,>=0.7.13 # BSD
-oslo.log>=3.22.0 # Apache-2.0
-oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0
-oslo.utils>=3.20.0 # Apache-2.0
-six>=1.9.0 # MIT
-tempest>=16.1.0 # Apache-2.0
+netaddr>=0.7.18 # BSD
+oslo.log>=3.36.0 # Apache-2.0
+oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
+oslo.utils>=3.33.0 # Apache-2.0
+six>=1.10.0 # MIT
+tempest>=17.1.0 # Apache-2.0
 ddt>=1.0.1 # MIT
-testtools>=1.4.0 # MIT
+testtools>=2.2.0 # MIT
 testscenarios>=0.4 # Apache-2.0/BSD
 eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 # MIT
diff --git a/setup.py b/setup.py
index 056c16c..566d844 100644
--- a/setup.py
+++ b/setup.py
@@ -25,5 +25,5 @@
     pass
 
 setuptools.setup(
-    setup_requires=['pbr'],
+    setup_requires=['pbr>=2.0.0'],
     pbr=True)
diff --git a/test-requirements.txt b/test-requirements.txt
index f559c0e..f4f8c0a 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -2,14 +2,14 @@
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
 
-hacking>=0.12.0,<0.13 # Apache-2.0
+hacking<0.13,>=0.12.0 # Apache-2.0
 
-coverage>=4.0,!=4.4 # Apache-2.0
-python-subunit>=0.0.18 # Apache-2.0/BSD
-sphinx>=1.6.2 # BSD
-oslotest>=1.10.0 # Apache-2.0
-testrepository>=0.0.18  # Apache-2.0/BSD
-testtools>=1.4.0 # MIT
-openstackdocstheme>=1.11.0  # Apache-2.0
+coverage!=4.4,>=4.0 # Apache-2.0
+python-subunit>=1.0.0 # Apache-2.0/BSD
+sphinx!=1.6.6,>=1.6.2 # BSD
+oslotest>=3.2.0 # Apache-2.0
+testrepository>=0.0.18 # Apache-2.0/BSD
+testtools>=2.2.0 # MIT
+openstackdocstheme>=1.18.1 # Apache-2.0
 # releasenotes
-reno>=1.8.0 # Apache-2.0
+reno>=2.5.0 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index dbd0526..c16664d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -33,7 +33,7 @@
   sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
 
 [testenv:debug]
-commands = oslo_debug_helper {posargs}
+commands = oslo_debug_helper -t neutron_tempest_plugin/ {posargs}
 
 [flake8]
 # E125 continuation line does not distinguish itself from next logical line