Merge "Updated from global requirements"
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index a4755cc..30628c0 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -1,14 +1,14 @@
 If you would like to contribute to the development of OpenStack, you must
 follow the steps in this page:
 
-   http://docs.openstack.org/infra/manual/developers.html
+   https://docs.openstack.org/infra/manual/developers.html
 
 If you already have a good understanding of how the system works and your
 OpenStack accounts are set up, you can skip to the development workflow
 section of this documentation to learn how changes to OpenStack should be
 submitted for review via the Gerrit tool:
 
-   http://docs.openstack.org/infra/manual/developers.html#development-workflow
+   https://docs.openstack.org/infra/manual/developers.html#development-workflow
 
 Pull requests submitted through GitHub will be ignored.
 
diff --git a/README.rst b/README.rst
index e81ffbe..b3883b8 100644
--- a/README.rst
+++ b/README.rst
@@ -7,6 +7,6 @@
 It contains the tempest plugin for the functional testing of Neutron Project.
 
 * Free software: Apache license
-* Documentation: http://docs.openstack.org/developer/neutron
-* Source: http://git.openstack.org/cgit/openstack/neutron-tempest-plugin
-* Bugs: http://bugs.launchpad.net/neutron
+* Documentation: https://docs.openstack.org/neutron/latest/
+* Source: https://git.openstack.org/cgit/openstack/neutron-tempest-plugin
+* Bugs: https://bugs.launchpad.net/neutron
diff --git a/neutron_tempest_plugin/api/admin/test_shared_network_extension.py b/neutron_tempest_plugin/api/admin/test_shared_network_extension.py
index 876bd32..16375ec 100644
--- a/neutron_tempest_plugin/api/admin/test_shared_network_extension.py
+++ b/neutron_tempest_plugin/api/admin/test_shared_network_extension.py
@@ -263,25 +263,9 @@
         with testtools.ExpectedException(lib_exc.Conflict):
             self.admin_client.delete_rbac_policy(res['policy']['id'])
 
-        # a wildcard policy should allow the specific policy to be deleted
-        # since it allows the remaining port
-        wild = self.admin_client.create_rbac_policy(
-            object_type='network', object_id=res['network']['id'],
-            action='access_as_shared', target_tenant='*')['rbac_policy']
-        self.admin_client.delete_rbac_policy(res['policy']['id'])
-
-        # now that wildcard is the only remaining, it should be subjected to
-        # to the same restriction
-        with testtools.ExpectedException(lib_exc.Conflict):
-            self.admin_client.delete_rbac_policy(wild['id'])
-        # similarly, we can't update the policy to a different tenant
-        with testtools.ExpectedException(lib_exc.Conflict):
-            self.admin_client.update_rbac_policy(
-                wild['id'], target_tenant=self.client2.tenant_id)
-
         self.client.delete_port(port['id'])
         # anchor is gone, delete should pass
-        self.admin_client.delete_rbac_policy(wild['id'])
+        self.admin_client.delete_rbac_policy(res['policy']['id'])
 
     @decorators.idempotent_id('34d627da-a732-68c0-2e1a-bc4a19246698')
     def test_delete_self_share_rule(self):
diff --git a/neutron_tempest_plugin/api/base.py b/neutron_tempest_plugin/api/base.py
index 7b333fe..51a7d3e 100644
--- a/neutron_tempest_plugin/api/base.py
+++ b/neutron_tempest_plugin/api/base.py
@@ -339,6 +339,8 @@
     @classmethod
     def create_port(cls, network, **kwargs):
         """Wrapper utility that returns a test port."""
+        if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
+            kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
         body = cls.client.create_port(network_id=network['id'],
                                       **kwargs)
         port = body['port']
diff --git a/neutron_tempest_plugin/scenario/constants.py b/neutron_tempest_plugin/scenario/constants.py
index 258c587..ddd45ff 100644
--- a/neutron_tempest_plugin/scenario/constants.py
+++ b/neutron_tempest_plugin/scenario/constants.py
@@ -15,4 +15,5 @@
 SERVER_STATUS_ACTIVE = 'ACTIVE'
 DEFAULT_SECURITY_GROUP = 'default'
 LIMIT_KILO_BITS_PER_SECOND = 1000
+LIMIT_KILO_BYTES = 1000
 SOCKET_CONNECT_TIMEOUT = 60
diff --git a/neutron_tempest_plugin/scenario/test_floatingip.py b/neutron_tempest_plugin/scenario/test_floatingip.py
index b253890..90e416d 100644
--- a/neutron_tempest_plugin/scenario/test_floatingip.py
+++ b/neutron_tempest_plugin/scenario/test_floatingip.py
@@ -14,6 +14,8 @@
 #    under the License.
 
 import netaddr
+from neutron_lib import constants as lib_constants
+from neutron_lib.services.qos import constants as qos_consts
 from tempest.common import utils
 from tempest.common import waiters
 from tempest.lib.common.utils import data_utils
@@ -21,12 +23,13 @@
 import testscenarios
 from testscenarios.scenarios import multiply_scenarios
 
-from neutron_lib import constants as lib_constants
+from neutron_tempest_plugin.api import base as base_api
 from neutron_tempest_plugin.common import ssh
 from neutron_tempest_plugin.common import utils as common_utils
 from neutron_tempest_plugin import config
 from neutron_tempest_plugin.scenario import base
 from neutron_tempest_plugin.scenario import constants
+from neutron_tempest_plugin.scenario import test_qos
 
 
 CONF = config.CONF
@@ -196,3 +199,62 @@
                                 proxy_client=proxy_client)
         self.check_remote_connectivity(ssh_client,
                                        gateway_external_ip)
+
+
+class FloatingIPQosTest(FloatingIpTestCasesMixin,
+                        test_qos.QoSTest):
+
+    same_network = True
+
+    @classmethod
+    @utils.requires_ext(extension="router", service="network")
+    @utils.requires_ext(extension="qos", service="network")
+    @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
+    def resource_setup(cls):
+        super(FloatingIPQosTest, cls).resource_setup()
+
+    @decorators.idempotent_id('5eb48aea-eaba-4c20-8a6f-7740070a0aa3')
+    def test_qos(self):
+        """Test floating IP is binding to a QoS policy with
+
+           ingress and egress bandwidth limit rules. And it applied correctly
+           by sending a file from the instance to the test node.
+           Then calculating the bandwidth every ~1 sec by the number of bits
+           received / elapsed time.
+        """
+
+        self._test_basic_resources()
+        policy_id = self._create_qos_policy()
+        ssh_client = self._create_ssh_client()
+        self.os_admin.network_client.create_bandwidth_limit_rule(
+            policy_id, max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
+            max_burst_kbps=constants.LIMIT_KILO_BYTES,
+            direction=lib_constants.INGRESS_DIRECTION)
+        self.os_admin.network_client.create_bandwidth_limit_rule(
+            policy_id, max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
+            max_burst_kbps=constants.LIMIT_KILO_BYTES,
+            direction=lib_constants.EGRESS_DIRECTION)
+
+        rules = self.os_admin.network_client.list_bandwidth_limit_rules(
+            policy_id)
+        self.assertEqual(2, len(rules['bandwidth_limit_rules']))
+
+        fip = self.os_admin.network_client.get_floatingip(
+            self.fip['id'])['floatingip']
+        self.assertEqual(self.port['id'], fip['port_id'])
+
+        self.os_admin.network_client.update_floatingip(
+            self.fip['id'],
+            qos_policy_id=policy_id)
+
+        fip = self.os_admin.network_client.get_floatingip(
+            self.fip['id'])['floatingip']
+        self.assertEqual(policy_id, fip['qos_policy_id'])
+
+        self._create_file_for_bw_tests(ssh_client)
+        common_utils.wait_until_true(lambda: self._check_bw(
+            ssh_client,
+            self.fip['floating_ip_address'],
+            port=self.NC_PORT),
+            timeout=120,
+            sleep=1)
diff --git a/neutron_tempest_plugin/scenario/test_qos.py b/neutron_tempest_plugin/scenario/test_qos.py
index 67c00c2..58accb0 100644
--- a/neutron_tempest_plugin/scenario/test_qos.py
+++ b/neutron_tempest_plugin/scenario/test_qos.py
@@ -79,6 +79,8 @@
                        * TOLERANCE_FACTOR / 8.0)
     FILE_PATH = "/tmp/img"
 
+    NC_PORT = 1234
+
     @classmethod
     @tutils.requires_ext(extension="qos", service="network")
     @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
@@ -117,7 +119,7 @@
         time_elapsed = time.time() - start_time
         bytes_per_second = total_bytes_read / time_elapsed
 
-        LOG.debug("time_elapsed = %(time_elapsed)d, "
+        LOG.debug("time_elapsed = %(time_elapsed).16f, "
                   "total_bytes_read = %(total_bytes_read)d, "
                   "bytes_per_second = %(bytes_per_second)d",
                   {'time_elapsed': time_elapsed,
@@ -126,6 +128,31 @@
 
         return bytes_per_second <= QoSTest.LIMIT_BYTES_SEC
 
+    def _create_ssh_client(self):
+        return ssh.Client(self.fip['floating_ip_address'],
+                          CONF.validation.image_ssh_user,
+                          pkey=self.keypair['private_key'])
+
+    def _test_basic_resources(self):
+        self.setup_network_and_server()
+        self.check_connectivity(self.fip['floating_ip_address'],
+                                CONF.validation.image_ssh_user,
+                                self.keypair['private_key'])
+        rulesets = [{'protocol': 'tcp',
+                     'direction': 'ingress',
+                     'port_range_min': self.NC_PORT,
+                     'port_range_max': self.NC_PORT,
+                     'remote_ip_prefix': '0.0.0.0/0'}]
+        self.create_secgroup_rules(rulesets,
+                                   self.security_groups[-1]['id'])
+
+    def _create_qos_policy(self):
+        policy = self.os_admin.network_client.create_qos_policy(
+                                        name='test-policy',
+                                        description='test-qos-policy',
+                                        shared=True)
+        return policy['policy']['id']
+
     @decorators.idempotent_id('1f7ed39b-428f-410a-bd2b-db9f465680df')
     def test_qos(self):
         """This is a basic test that check that a QoS policy with
@@ -135,29 +162,9 @@
            Then calculating the bandwidth every ~1 sec by the number of bits
            received / elapsed time.
         """
-
-        NC_PORT = 1234
-
-        self.setup_network_and_server()
-        self.check_connectivity(self.fip['floating_ip_address'],
-                                CONF.validation.image_ssh_user,
-                                self.keypair['private_key'])
-        rulesets = [{'protocol': 'tcp',
-                     'direction': 'ingress',
-                     'port_range_min': NC_PORT,
-                     'port_range_max': NC_PORT,
-                     'remote_ip_prefix': '0.0.0.0/0'}]
-        self.create_secgroup_rules(rulesets,
-                                   self.security_groups[-1]['id'])
-
-        ssh_client = ssh.Client(self.fip['floating_ip_address'],
-                                CONF.validation.image_ssh_user,
-                                pkey=self.keypair['private_key'])
-        policy = self.os_admin.network_client.create_qos_policy(
-                                        name='test-policy',
-                                        description='test-qos-policy',
-                                        shared=True)
-        policy_id = policy['policy']['id']
+        self._test_basic_resources()
+        policy_id = self._create_qos_policy()
+        ssh_client = self._create_ssh_client()
         self.os_admin.network_client.create_bandwidth_limit_rule(
             policy_id, max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
             max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)
@@ -170,6 +177,6 @@
         utils.wait_until_true(lambda: self._check_bw(
             ssh_client,
             self.fip['floating_ip_address'],
-            port=NC_PORT),
+            port=self.NC_PORT),
             timeout=120,
             sleep=1)
diff --git a/neutron_tempest_plugin/services/network/json/network_client.py b/neutron_tempest_plugin/services/network/json/network_client.py
index a48db36..930cbfd 100644
--- a/neutron_tempest_plugin/services/network/json/network_client.py
+++ b/neutron_tempest_plugin/services/network/json/network_client.py
@@ -910,6 +910,23 @@
         body = jsonutils.loads(body)
         return service_client.ResponseBody(resp, body)
 
+    def get_floatingip(self, fip_id):
+        uri = '%s/floatingips/%s' % (self.uri_prefix, fip_id)
+        get_resp, get_resp_body = self.get(uri)
+        self.expected_success(200, get_resp.status)
+        body = jsonutils.loads(get_resp_body)
+        return service_client.ResponseBody(get_resp, body)
+
+    def update_floatingip(self, fip_id, **kwargs):
+        uri = '%s/floatingips/%s' % (self.uri_prefix, fip_id)
+        get_resp, _ = self.get(uri)
+        self.expected_success(200, get_resp.status)
+        put_body = jsonutils.dumps({'floatingip': kwargs})
+        put_resp, resp_body = self.put(uri, put_body)
+        self.expected_success(200, put_resp.status)
+        body = jsonutils.loads(resp_body)
+        return service_client.ResponseBody(put_resp, body)
+
     def create_network_keystone_v3(self, name, project_id, tenant_id=None):
         uri = '%s/networks' % self.uri_prefix
         post_data = {
diff --git a/setup.cfg b/setup.cfg
index b0b7405..a54cc6c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@
     README.rst
 author = OpenStack
 author-email = openstack-dev@lists.openstack.org
-home-page = http://www.openstack.org/
+home-page = https://git.openstack.org/cgit/openstack/neutron-tempest-plugin
 classifier =
     Environment :: OpenStack
     Intended Audience :: Information Technology