Merge "Use more specific asserts in tests"
diff --git a/neutron/tests/tempest/api/admin/test_networks.py b/neutron/tests/tempest/api/admin/test_networks.py
new file mode 100644
index 0000000..1068c0b
--- /dev/null
+++ b/neutron/tests/tempest/api/admin/test_networks.py
@@ -0,0 +1,70 @@
+# 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 import exceptions as lib_exc
+from tempest import test
+
+from neutron.tests.tempest.api import base
+
+
+class NetworksTestAdmin(base.BaseAdminNetworkTest):
+
+ @test.idempotent_id('d3c76044-d067-4cb0-ae47-8cdd875c7f67')
+ @test.requires_ext(extension="project-id", service="network")
+ def test_admin_create_network_keystone_v3(self):
+ project_id = self.client.tenant_id # non-admin
+
+ name = 'admin-created-with-project_id'
+ new_net = self.create_network_keystone_v3(name, project_id,
+ client=self.admin_client)
+ self.assertEqual(name, new_net['name'])
+ self.assertEqual(project_id, new_net['project_id'])
+ self.assertEqual(project_id, new_net['tenant_id'])
+
+ body = self.client.list_networks(id=new_net['id'])
+ lookup_net = body['networks'][0]
+ self.assertEqual(name, lookup_net['name'])
+ self.assertEqual(project_id, lookup_net['project_id'])
+ self.assertEqual(project_id, lookup_net['tenant_id'])
+
+ @test.idempotent_id('8d21aaca-4364-4eb9-8b79-44b4fff6373b')
+ @test.requires_ext(extension="project-id", service="network")
+ def test_admin_create_network_keystone_v3_and_tenant(self):
+ project_id = self.client.tenant_id # non-admin
+
+ name = 'created-with-project-and-tenant'
+ new_net = self.create_network_keystone_v3(
+ name, project_id, tenant_id=project_id, client=self.admin_client)
+ self.assertEqual(name, new_net['name'])
+ self.assertEqual(project_id, new_net['project_id'])
+ self.assertEqual(project_id, new_net['tenant_id'])
+
+ body = self.client.list_networks(id=new_net['id'])
+ lookup_net = body['networks'][0]
+ self.assertEqual(name, lookup_net['name'])
+ self.assertEqual(project_id, lookup_net['project_id'])
+ self.assertEqual(project_id, lookup_net['tenant_id'])
+
+ @test.idempotent_id('08b92179-669d-45ee-8233-ef6611190809')
+ @test.requires_ext(extension="project-id", service="network")
+ def test_admin_create_network_keystone_v3_and_other_tenant(self):
+ project_id = self.client.tenant_id # non-admin
+ other_tenant = uuidutils.generate_uuid()
+
+ name = 'created-with-project-and-other-tenant'
+ e = self.assertRaises(lib_exc.BadRequest,
+ self.create_network_keystone_v3, name,
+ project_id, tenant_id=other_tenant,
+ client=self.admin_client)
+ expected_message = "'project_id' and 'tenant_id' do not match"
+ self.assertEqual(expected_message, e.resp_body['message'])
diff --git a/neutron/tests/tempest/api/admin/test_quotas_negative.py b/neutron/tests/tempest/api/admin/test_quotas_negative.py
index 12ae0be..3ff49c6 100644
--- a/neutron/tests/tempest/api/admin/test_quotas_negative.py
+++ b/neutron/tests/tempest/api/admin/test_quotas_negative.py
@@ -71,6 +71,7 @@
subnet_args = {'tenant_id': tenant_id,
'network_id': net['id'],
+ 'enable_dhcp': False,
'cidr': '10.0.0.0/24',
'ip_version': '4'}
subnet = self.admin_client.create_subnet(**subnet_args)['subnet']
diff --git a/neutron/tests/tempest/api/admin/test_routers_flavors.py b/neutron/tests/tempest/api/admin/test_routers_flavors.py
new file mode 100644
index 0000000..300c956
--- /dev/null
+++ b/neutron/tests/tempest/api/admin/test_routers_flavors.py
@@ -0,0 +1,97 @@
+#
+# 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 import exceptions as lib_exc
+from tempest import test
+import testtools
+
+from neutron.tests.tempest.api import base_routers as base
+
+
+class RoutersFlavorTestCase(base.BaseRouterTest):
+
+ @classmethod
+ @test.requires_ext(extension="router", service="network")
+ @test.requires_ext(extension="flavors", service="network")
+ def skip_checks(cls):
+ super(RoutersFlavorTestCase, cls).skip_checks()
+
+ @classmethod
+ def resource_setup(cls):
+ super(RoutersFlavorTestCase, cls).resource_setup()
+ cls.service_profiles = []
+ cls.flavor_service_profiles = []
+ # make a flavor based on legacy router for regular tenant to use
+ driver = ('neutron.services.l3_router.service_providers.'
+ 'single_node.SingleNodeDriver')
+ sp = cls.admin_client.create_service_profile(driver=driver)
+ cls.service_profiles.append(sp['service_profile'])
+ cls.flavor = cls.create_flavor(
+ name='special_flavor',
+ description='econonomy class',
+ service_type='L3_ROUTER_NAT')
+ cls.admin_client.create_flavor_service_profile(
+ cls.flavor['id'], sp['service_profile']['id'])
+ cls.flavor_service_profiles.append((cls.flavor['id'],
+ sp['service_profile']['id']))
+ # make another with a different driver
+ driver = ('neutron.services.l3_router.service_providers.'
+ 'dvr.DvrDriver')
+ sp = cls.admin_client.create_service_profile(driver=driver)
+ cls.service_profiles.append(sp['service_profile'])
+ cls.prem_flavor = cls.create_flavor(
+ name='better_special_flavor',
+ description='econonomy comfort',
+ service_type='L3_ROUTER_NAT')
+ cls.admin_client.create_flavor_service_profile(
+ cls.prem_flavor['id'], sp['service_profile']['id'])
+ cls.flavor_service_profiles.append((cls.prem_flavor['id'],
+ sp['service_profile']['id']))
+
+ @classmethod
+ def resource_cleanup(cls):
+ for flavor_id, service_profile_id in cls.flavor_service_profiles:
+ cls.admin_client.delete_flavor_service_profile(flavor_id,
+ service_profile_id)
+ for service_profile in cls.service_profiles:
+ cls.admin_client.delete_service_profile(
+ service_profile['id'])
+ super(RoutersFlavorTestCase, cls).resource_cleanup()
+
+ @test.idempotent_id('a4d01977-e968-4983-b4d9-824ea6c33f4b')
+ def test_create_router_with_flavor(self):
+ # ensure regular client can see flavor
+ flavors = self.client.list_flavors(id=self.flavor['id'])
+ flavor = flavors['flavors'][0]
+ self.assertEqual('special_flavor', flavor['name'])
+ flavors = self.client.list_flavors(id=self.prem_flavor['id'])
+ prem_flavor = flavors['flavors'][0]
+ self.assertEqual('better_special_flavor', prem_flavor['name'])
+
+ # ensure client can create router with both flavors
+ router = self.create_router('name', flavor_id=flavor['id'])
+ self.assertEqual(flavor['id'], router['flavor_id'])
+ router = self.create_router('name', flavor_id=prem_flavor['id'])
+ self.assertEqual(prem_flavor['id'], router['flavor_id'])
+
+ @test.idempotent_id('30e73858-a0fc-409c-a2e0-e9cd2826f6a2')
+ def test_delete_router_flavor_in_use(self):
+ self.create_router('name', flavor_id=self.flavor['id'])
+ with testtools.ExpectedException(lib_exc.Conflict):
+ self.admin_client.delete_flavor(self.flavor['id'])
+
+ @test.idempotent_id('83939cf7-5070-41bc-9a3e-cd9f22df2186')
+ def test_badrequest_on_requesting_flags_and_flavor(self):
+ with testtools.ExpectedException(lib_exc.BadRequest):
+ self.admin_client.create_router(
+ 'name', flavor_id=self.flavor['id'], distributed=True)
diff --git a/neutron/tests/tempest/api/admin/test_shared_network_extension.py b/neutron/tests/tempest/api/admin/test_shared_network_extension.py
index a637c3e..bc10745 100644
--- a/neutron/tests/tempest/api/admin/test_shared_network_extension.py
+++ b/neutron/tests/tempest/api/admin/test_shared_network_extension.py
@@ -192,6 +192,22 @@
)['rbac_policy']
return {'network': net, 'subnet': subnet, 'policy': pol}
+ @test.attr(type='smoke')
+ @test.idempotent_id('86c3529b-1231-40de-803c-bfffffff1eee')
+ def test_create_rbac_policy_with_target_tenant_none(self):
+ with testtools.ExpectedException(lib_exc.BadRequest):
+ self._make_admin_net_and_subnet_shared_to_tenant_id(
+ tenant_id=None)
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('86c3529b-1231-40de-803c-bfffffff1fff')
+ def test_create_rbac_policy_with_target_tenant_too_long_id(self):
+ with testtools.ExpectedException(lib_exc.BadRequest):
+ target_tenant = '1234' * 100
+ self._make_admin_net_and_subnet_shared_to_tenant_id(
+ tenant_id=target_tenant)
+
+ @test.attr(type='smoke')
@test.idempotent_id('86c3529b-1231-40de-803c-afffffff1fff')
def test_network_only_visible_to_policy_target(self):
net = self._make_admin_net_and_subnet_shared_to_tenant_id(
diff --git a/neutron/tests/tempest/api/base.py b/neutron/tests/tempest/api/base.py
index c4d3b48..b308a31 100644
--- a/neutron/tests/tempest/api/base.py
+++ b/neutron/tests/tempest/api/base.py
@@ -93,7 +93,7 @@
super(BaseNetworkTest, cls).resource_setup()
cls.networks = []
- cls.shared_networks = []
+ cls.admin_networks = []
cls.subnets = []
cls.ports = []
cls.routers = []
@@ -162,8 +162,8 @@
cls._try_delete_resource(cls.client.delete_network,
network['id'])
- # Clean up shared networks
- for network in cls.shared_networks:
+ # Clean up admin networks
+ for network in cls.admin_networks:
cls._try_delete_resource(cls.admin_client.delete_network,
network['id'])
@@ -222,7 +222,24 @@
post_body.update({'name': network_name, 'shared': True})
body = cls.admin_client.create_network(**post_body)
network = body['network']
- cls.shared_networks.append(network)
+ cls.admin_networks.append(network)
+ return network
+
+ @classmethod
+ def create_network_keystone_v3(cls, network_name=None, project_id=None,
+ tenant_id=None, client=None):
+ """Wrapper utility that creates a test network with project_id."""
+ client = client or cls.client
+ network_name = network_name or data_utils.rand_name(
+ 'test-network-with-project_id')
+ project_id = cls.client.tenant_id
+ body = client.create_network_keystone_v3(network_name, project_id,
+ tenant_id)
+ network = body['network']
+ if client is cls.client:
+ cls.networks.append(network)
+ else:
+ cls.admin_networks.append(network)
return network
@classmethod
@@ -728,3 +745,12 @@
# marker
expected_resources[:-1],
self._extract_resources(body))
+
+ def _test_list_validation_filters(self):
+ validation_args = {
+ 'unknown_filter': 'value',
+ }
+ body = self.list_method(**validation_args)
+ resources = self._extract_resources(body)
+ for resource in resources:
+ self.assertIn(resource['name'], self.resource_names)
diff --git a/neutron/tests/tempest/api/base_security_groups.py b/neutron/tests/tempest/api/base_security_groups.py
index 8575ba9..5028ec1 100644
--- a/neutron/tests/tempest/api/base_security_groups.py
+++ b/neutron/tests/tempest/api/base_security_groups.py
@@ -20,10 +20,6 @@
class BaseSecGroupTest(base.BaseNetworkTest):
- @classmethod
- def resource_setup(cls):
- super(BaseSecGroupTest, cls).resource_setup()
-
def _create_security_group(self, **kwargs):
# Create a security group
name = data_utils.rand_name('secgroup-')
diff --git a/neutron/tests/tempest/api/clients.py b/neutron/tests/tempest/api/clients.py
index eb39882..c6f41d0 100644
--- a/neutron/tests/tempest/api/clients.py
+++ b/neutron/tests/tempest/api/clients.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from oslo_config import cfg
-
from tempest.lib.services.compute import keypairs_client
from tempest.lib.services.compute import servers_client
from tempest.lib.services.identity.v2 import tenants_client
@@ -30,22 +28,12 @@
"""
Top level manager for OpenStack tempest clients
"""
- try:
- default_params = {
- 'disable_ssl_certificate_validation':
- CONF.service_clients.disable_ssl_certificate_validation,
- 'ca_certs': CONF.service_clients.ca_certificates_file,
- 'trace_requests': CONF.debug.trace_requests
- }
- except cfg.NoSuchOptError:
- # TODO(armax): remove this except when a new tempest release
- # > 12.1.0 includes change 1afca56b05
- default_params = {
- 'disable_ssl_certificate_validation':
- CONF.identity.disable_ssl_certificate_validation,
- 'ca_certs': CONF.identity.ca_certificates_file,
- 'trace_requests': CONF.debug.trace_requests
- }
+ default_params = {
+ 'disable_ssl_certificate_validation':
+ CONF.identity.disable_ssl_certificate_validation,
+ 'ca_certs': CONF.identity.ca_certificates_file,
+ 'trace_requests': CONF.debug.trace_requests
+ }
# NOTE: Tempest uses timeout values of compute API if project specific
# timeout values don't exist.
diff --git a/neutron/tests/tempest/api/test_allowed_address_pair.py b/neutron/tests/tempest/api/test_allowed_address_pair.py
index 88818b2..5313785 100644
--- a/neutron/tests/tempest/api/test_allowed_address_pair.py
+++ b/neutron/tests/tempest/api/test_allowed_address_pair.py
@@ -19,8 +19,6 @@
from neutron.tests.tempest.api import base
from neutron.tests.tempest import config
-CONF = config.CONF
-
class AllowedAddressPairTestJSON(base.BaseNetworkTest):
@@ -85,7 +83,7 @@
body = self.client.update_port(
port_id, allowed_address_pairs=allowed_address_pairs)
allowed_address_pair = body['port']['allowed_address_pairs']
- self.assertEqual(allowed_address_pair, allowed_address_pairs)
+ self.assertItemsEqual(allowed_address_pair, allowed_address_pairs)
@test.idempotent_id('9599b337-272c-47fd-b3cf-509414414ac4')
def test_update_port_with_address_pair(self):
diff --git a/neutron/tests/tempest/api/test_auto_allocated_topology.py b/neutron/tests/tempest/api/test_auto_allocated_topology.py
index afdfe8c..65c3057 100644
--- a/neutron/tests/tempest/api/test_auto_allocated_topology.py
+++ b/neutron/tests/tempest/api/test_auto_allocated_topology.py
@@ -22,11 +22,14 @@
class TestAutoAllocatedTopology(base.BaseAdminNetworkTest):
"""
- NOTE: This test may eventually migrate to Tempest.
-
- Tests the Get-Me-A-Network operation in the Neutron API
+ Tests the Get-Me-A-Network operations in the Neutron API
using the REST client for Neutron.
"""
+ # NOTE(armax): this is a precaution to avoid interference
+ # from other tests exercising this extension. So long as
+ # all tests are added under TestAutoAllocatedTopology,
+ # nothing bad should happen.
+ force_tenant_isolation = True
@classmethod
@test.requires_ext(extension="auto-allocated-topology", service="network")
@@ -101,3 +104,14 @@
# After the initial GET, the API should be idempotent
self.assertEqual(network_id1, network_id2)
self.assertEqual(resources_after1, resources_after2)
+
+ @test.idempotent_id('aabc0b02-cee4-11e5-9f3c-091127605a2b')
+ def test_delete_allocated_net_topology_as_tenant(self):
+ resources_before = self._count_topology_resources()
+ self.assertEqual((0, 0, 0), resources_before)
+ body = self.client.get_auto_allocated_topology()
+ topology = body['auto_allocated_topology']
+ self.assertIsNotNone(topology)
+ self.client.delete_auto_allocated_topology()
+ resources_after = self._count_topology_resources()
+ self.assertEqual((0, 0, 0), resources_after)
diff --git a/neutron/tests/tempest/api/test_extensions.py b/neutron/tests/tempest/api/test_extensions.py
index 1defb33..b1b09e8 100644
--- a/neutron/tests/tempest/api/test_extensions.py
+++ b/neutron/tests/tempest/api/test_extensions.py
@@ -13,9 +13,6 @@
from tempest import test
from neutron.tests.tempest.api import base
-from neutron.tests.tempest import config
-
-CONF = config.CONF
class ExtensionsTest(base.BaseNetworkTest):
@@ -37,3 +34,7 @@
@test.idempotent_id('19db409e-a23f-445d-8bc8-ca3d64c84706')
def test_list_extensions_pagination(self):
self._test_list_extensions_includes('pagination')
+
+ @test.idempotent_id('155b7bc2-e358-4dd8-bf3e-1774c084567f')
+ def test_list_extensions_project_id(self):
+ self._test_list_extensions_includes('project-id')
diff --git a/neutron/tests/tempest/api/test_floating_ips.py b/neutron/tests/tempest/api/test_floating_ips.py
index 8ccdd44..bafa54c 100644
--- a/neutron/tests/tempest/api/test_floating_ips.py
+++ b/neutron/tests/tempest/api/test_floating_ips.py
@@ -71,3 +71,33 @@
self.assertEqual('d2', body['floatingip']['description'])
body = self.client.show_floatingip(body['floatingip']['id'])
self.assertEqual('d2', body['floatingip']['description'])
+ # disassociate
+ body = self.client.update_floatingip(body['floatingip']['id'],
+ port_id=None)
+ self.assertEqual('d2', body['floatingip']['description'])
+
+ @test.idempotent_id('fd7161e1-2167-4686-a6ff-0f3df08001bb')
+ @test.requires_ext(extension="standard-attr-description",
+ service="network")
+ def test_floatingip_update_extra_attributes_port_id_not_changed(self):
+ port_id = self.ports[1]['id']
+ body = self.client.create_floatingip(
+ floating_network_id=self.ext_net_id,
+ port_id=port_id,
+ description='d1'
+ )['floatingip']
+ self.assertEqual('d1', body['description'])
+ body = self.client.show_floatingip(body['id'])['floatingip']
+ self.assertEqual(port_id, body['port_id'])
+ # Update description
+ body = self.client.update_floatingip(body['id'], description='d2')
+ self.assertEqual('d2', body['floatingip']['description'])
+ # Floating IP association is not changed.
+ self.assertEqual(port_id, body['floatingip']['port_id'])
+ body = self.client.show_floatingip(body['floatingip']['id'])
+ self.assertEqual('d2', body['floatingip']['description'])
+ self.assertEqual(port_id, body['floatingip']['port_id'])
+ # disassociate
+ body = self.client.update_floatingip(body['floatingip']['id'],
+ port_id=None)
+ self.assertEqual(None, body['floatingip']['port_id'])
diff --git a/neutron/tests/tempest/api/test_metering_extensions.py b/neutron/tests/tempest/api/test_metering_extensions.py
index 756cd5a..7b03386 100644
--- a/neutron/tests/tempest/api/test_metering_extensions.py
+++ b/neutron/tests/tempest/api/test_metering_extensions.py
@@ -15,8 +15,11 @@
from tempest.lib.common.utils import data_utils
from tempest import test
+from neutron.api.v2 import attributes as attr
from neutron.tests.tempest.api import base
+LONG_NAME_OK = 'x' * (attr.NAME_MAX_LEN)
+
class MeteringTestJSON(base.BaseAdminNetworkTest):
@@ -81,6 +84,17 @@
id=metering_label['id']))
self.assertEqual(len(labels['metering_labels']), 1)
+ @test.idempotent_id('46608f8d-2e27-4eb6-a0b4-dbe405144c4d')
+ def test_create_delete_metering_label_with_name_max_length(self):
+ name = LONG_NAME_OK
+ body = self.admin_client.create_metering_label(name=name)
+ metering_label = body['metering_label']
+ self.addCleanup(self._delete_metering_label,
+ metering_label['id'])
+ labels = (self.admin_client.list_metering_labels(
+ id=metering_label['id']))
+ self.assertEqual(len(labels['metering_labels']), 1)
+
@test.idempotent_id('cfc500d9-9de6-4847-8803-62889c097d45')
def test_show_metering_label(self):
# Verifies the details of a label
diff --git a/neutron/tests/tempest/api/test_metering_negative.py b/neutron/tests/tempest/api/test_metering_negative.py
new file mode 100644
index 0000000..39fdae8
--- /dev/null
+++ b/neutron/tests/tempest/api/test_metering_negative.py
@@ -0,0 +1,36 @@
+# Copyright 2016 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 import exceptions as lib_exc
+from tempest import test
+
+from neutron.api.v2 import attributes as attr
+from neutron.tests.tempest.api import base
+
+LONG_NAME_NG = 'x' * (attr.NAME_MAX_LEN + 1)
+
+
+class MeteringNegativeTestJSON(base.BaseAdminNetworkTest):
+
+ @classmethod
+ @test.requires_ext(extension="metering", service="network")
+ def resource_setup(cls):
+ super(MeteringNegativeTestJSON, cls).resource_setup()
+
+ @test.attr(type='negative')
+ @test.idempotent_id('8b3f7c84-9d37-4771-8681-bfd2c07f3c2d')
+ def test_create_metering_label_with_too_long_name(self):
+ self.assertRaises(lib_exc.BadRequest,
+ self.admin_client.create_metering_label,
+ name=LONG_NAME_NG)
diff --git a/neutron/tests/tempest/api/test_network_ip_availability.py b/neutron/tests/tempest/api/test_network_ip_availability.py
index 6a81128..324c3fd 100644
--- a/neutron/tests/tempest/api/test_network_ip_availability.py
+++ b/neutron/tests/tempest/api/test_network_ip_availability.py
@@ -24,8 +24,6 @@
from neutron_lib import constants as lib_constants
-CONF = config.CONF
-
# 3 IP addresses are taken from every total for IPv4 these are reserved
DEFAULT_IP4_RESERVED = 3
# 2 IP addresses are taken from every total for IPv6 these are reserved
diff --git a/neutron/tests/tempest/api/test_networks.py b/neutron/tests/tempest/api/test_networks.py
index 16fe81b..279964a 100644
--- a/neutron/tests/tempest/api/test_networks.py
+++ b/neutron/tests/tempest/api/test_networks.py
@@ -16,9 +16,6 @@
from tempest import test
from neutron.tests.tempest.api import base
-from neutron.tests.tempest import config
-
-CONF = config.CONF
class NetworksTestJSON(base.BaseNetworkTest):
@@ -49,6 +46,10 @@
fields.append('mtu')
for key in fields:
self.assertEqual(network[key], self.network[key])
+ project_id = self.client.tenant_id
+ self.assertEqual(project_id, network['tenant_id'])
+ if test.is_extension_enabled('project-id', 'network'):
+ self.assertEqual(project_id, network['project_id'])
@test.idempotent_id('867819bb-c4b6-45f7-acf9-90edcf70aa5e')
def test_show_network_fields(self):
@@ -62,6 +63,27 @@
self.assertEqual(sorted(network.keys()), sorted(fields))
for field_name in fields:
self.assertEqual(network[field_name], self.network[field_name])
+ self.assertNotIn('tenant_id', network)
+ self.assertNotIn('project_id', network)
+
+ @test.idempotent_id('26f2b7a5-2cd1-4f3a-b11f-ad259b099b11')
+ @test.requires_ext(extension="project-id", service="network")
+ def test_show_network_fields_keystone_v3(self):
+
+ def _check_show_network_fields(fields, expect_project_id,
+ expect_tenant_id):
+ params = {}
+ if fields:
+ params['fields'] = fields
+ body = self.client.show_network(self.network['id'], **params)
+ network = body['network']
+ self.assertEqual(expect_project_id, 'project_id' in network)
+ self.assertEqual(expect_tenant_id, 'tenant_id' in network)
+
+ _check_show_network_fields(None, True, True)
+ _check_show_network_fields(['tenant_id'], False, True)
+ _check_show_network_fields(['project_id'], True, False)
+ _check_show_network_fields(['project_id', 'tenant_id'], True, True)
@test.idempotent_id('c72c1c0c-2193-4aca-ccc4-b1442640bbbb')
@test.requires_ext(extension="standard-attr-description",
@@ -78,6 +100,27 @@
body = self.client.list_networks(id=net_id)['networks'][0]
self.assertEqual('d2', body['description'])
+ @test.idempotent_id('0cc0552f-afaf-4231-b7a7-c2a1774616da')
+ @test.requires_ext(extension="project-id", service="network")
+ def test_create_network_keystone_v3(self):
+ project_id = self.client.tenant_id
+
+ name = 'created-with-project_id'
+ new_net = self.create_network_keystone_v3(name, project_id)
+ self.assertEqual(name, new_net['name'])
+ self.assertEqual(project_id, new_net['project_id'])
+ self.assertEqual(project_id, new_net['tenant_id'])
+
+ body = self.client.list_networks(id=new_net['id'])['networks'][0]
+ self.assertEqual(name, body['name'])
+
+ new_name = 'create-with-project_id-2'
+ body = self.client.update_network(new_net['id'], name=new_name)
+ new_net = body['network']
+ self.assertEqual(new_name, new_net['name'])
+ self.assertEqual(project_id, new_net['project_id'])
+ self.assertEqual(project_id, new_net['tenant_id'])
+
@test.idempotent_id('6ae6d24f-9194-4869-9c85-c313cb20e080')
def test_list_networks_fields(self):
# Verify specific fields of the networks
@@ -90,12 +133,32 @@
for network in networks:
self.assertEqual(sorted(network.keys()), sorted(fields))
+ @test.idempotent_id('a23186b9-aa6f-4b08-b877-35ca3b9cd54c')
+ @test.requires_ext(extension="project-id", service="network")
+ def test_list_networks_fields_keystone_v3(self):
+ def _check_list_networks_fields(fields, expect_project_id,
+ expect_tenant_id):
+ params = {}
+ if fields:
+ params['fields'] = fields
+ body = self.client.list_networks(**params)
+ networks = body['networks']
+ self.assertNotEmpty(networks, "Network list returned is empty")
+ for network in networks:
+ self.assertEqual(expect_project_id, 'project_id' in network)
+ self.assertEqual(expect_tenant_id, 'tenant_id' in network)
+
+ _check_list_networks_fields(None, True, True)
+ _check_list_networks_fields(['tenant_id'], False, True)
+ _check_list_networks_fields(['project_id'], True, False)
+ _check_list_networks_fields(['project_id', 'tenant_id'], True, True)
+
class NetworksSearchCriteriaTest(base.BaseSearchCriteriaTest):
resource = 'network'
- list_kwargs = {'shared': False}
+ list_kwargs = {'shared': False, 'router:external': False}
@classmethod
def resource_setup(cls):
@@ -138,3 +201,7 @@
@test.idempotent_id('f1867fc5-e1d6-431f-bc9f-8b882e43a7f9')
def test_list_no_pagination_limit_0(self):
self._test_list_no_pagination_limit_0()
+
+ @test.idempotent_id('3574ec9b-a8b8-43e3-9c11-98f5875df6a9')
+ def test_list_validation_filters(self):
+ self._test_list_validation_filters()
diff --git a/neutron/tests/tempest/api/test_ports.py b/neutron/tests/tempest/api/test_ports.py
index 916f549..093de07 100644
--- a/neutron/tests/tempest/api/test_ports.py
+++ b/neutron/tests/tempest/api/test_ports.py
@@ -47,6 +47,18 @@
self.client.update_subnet(s['id'], enable_dhcp=True)
self.create_port(self.network)
+ @test.idempotent_id('1d6d8683-8691-43c6-a7ba-c69723258726')
+ def test_add_ips_to_port(self):
+ s = self.create_subnet(self.network)
+ port = self.create_port(self.network)
+ # request another IP on the same subnet
+ port['fixed_ips'].append({'subnet_id': s['id']})
+ updated = self.client.update_port(port['id'],
+ fixed_ips=port['fixed_ips'])
+ subnets = [ip['subnet_id'] for ip in updated['port']['fixed_ips']]
+ expected = [s['id'], s['id']]
+ self.assertEqual(expected, subnets)
+
class PortsSearchCriteriaTest(base.BaseSearchCriteriaTest):
diff --git a/neutron/tests/tempest/api/test_qos.py b/neutron/tests/tempest/api/test_qos.py
index 313715d..b9572ca 100644
--- a/neutron/tests/tempest/api/test_qos.py
+++ b/neutron/tests/tempest/api/test_qos.py
@@ -75,6 +75,28 @@
self.assertTrue(retrieved_policy['shared'])
self.assertEqual([], retrieved_policy['rules'])
+ @test.idempotent_id('6e880e0f-bbfc-4e54-87c6-680f90e1b618')
+ def test_policy_update_forbidden_for_regular_tenants_own_policy(self):
+ policy = self.create_qos_policy(name='test-policy',
+ description='',
+ shared=False,
+ tenant_id=self.client.tenant_id)
+ self.assertRaises(
+ exceptions.Forbidden,
+ self.client.update_qos_policy,
+ policy['id'], description='test policy')
+
+ @test.idempotent_id('4ecfd7e7-47b6-4702-be38-be9235901a87')
+ def test_policy_update_forbidden_for_regular_tenants_foreign_policy(self):
+ policy = self.create_qos_policy(name='test-policy',
+ description='',
+ shared=False,
+ tenant_id=self.admin_client.tenant_id)
+ self.assertRaises(
+ exceptions.NotFound,
+ self.client.update_qos_policy,
+ policy['id'], description='test policy')
+
@test.idempotent_id('ee263db4-009a-4641-83e5-d0e83506ba4c')
def test_shared_policy_update(self):
policy = self.create_qos_policy(name='test-policy',
@@ -315,6 +337,13 @@
obtained_policy = self.client.show_qos_policy(policy['id'])['policy']
self.assertEqual(obtained_policy, policy)
+ @test.idempotent_id('aed8e2a6-22da-421b-89b9-935a2c1a1b50')
+ def test_policy_create_forbidden_for_regular_tenants(self):
+ self.assertRaises(
+ exceptions.Forbidden,
+ self.client.create_qos_policy,
+ 'test-policy', 'test policy', False)
+
class QosBandwidthLimitRuleTestJSON(base.BaseAdminNetworkTest):
@classmethod
@@ -412,13 +441,6 @@
self.create_qos_bandwidth_limit_rule,
'policy', 200, 1337)
- @test.idempotent_id('eed8e2a6-22da-421b-89b9-935a2c1a1b50')
- def test_policy_create_forbidden_for_regular_tenants(self):
- self.assertRaises(
- exceptions.Forbidden,
- self.client.create_qos_policy,
- 'test-policy', 'test policy', False)
-
@test.idempotent_id('a4a2e7ad-786f-4927-a85a-e545a93bd274')
def test_rule_create_forbidden_for_regular_tenants(self):
self.assertRaises(
@@ -426,6 +448,34 @@
self.client.create_bandwidth_limit_rule,
'policy', 1, 2)
+ @test.idempotent_id('1bfc55d9-6fd8-4293-ab3a-b1d69bf7cd2e')
+ def test_rule_update_forbidden_for_regular_tenants_own_policy(self):
+ policy = self.create_qos_policy(name='test-policy',
+ description='test policy',
+ shared=False,
+ tenant_id=self.client.tenant_id)
+ rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
+ max_kbps=1,
+ max_burst_kbps=1)
+ self.assertRaises(
+ exceptions.NotFound,
+ self.client.update_bandwidth_limit_rule,
+ policy['id'], rule['id'], max_kbps=2, max_burst_kbps=4)
+
+ @test.idempotent_id('9a607936-4b6f-4c2f-ad21-bd5b3d4fc91f')
+ def test_rule_update_forbidden_for_regular_tenants_foreign_policy(self):
+ policy = self.create_qos_policy(name='test-policy',
+ description='test policy',
+ shared=False,
+ tenant_id=self.admin_client.tenant_id)
+ rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
+ max_kbps=1,
+ max_burst_kbps=1)
+ self.assertRaises(
+ exceptions.NotFound,
+ self.client.update_bandwidth_limit_rule,
+ policy['id'], rule['id'], max_kbps=2, max_burst_kbps=4)
+
@test.idempotent_id('ce0bd0c2-54d9-4e29-85f1-cfb36ac3ebe2')
def test_get_rules_by_policy(self):
policy1 = self.create_qos_policy(name='test-policy1',
@@ -504,6 +554,7 @@
self.client2.show_qos_policy(qos_pol['id'])
rbac_pol = {'target_tenant': '*',
'tenant_id': self.admin_client.tenant_id,
+ 'project_id': self.admin_client.tenant_id,
'object_type': 'qos_policy',
'object_id': qos_pol['id'],
'action': 'access_as_shared'}
@@ -833,6 +884,162 @@
self.assertNotIn(rule2['id'], rules_ids)
+class QosMinimumBandwidthRuleTestJSON(base.BaseAdminNetworkTest):
+ DIRECTION_EGRESS = "egress"
+ DIRECTION_INGRESS = "ingress"
+ RULE_NAME = qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH + "_rule"
+ RULES_NAME = RULE_NAME + "s"
+
+ @classmethod
+ @test.requires_ext(extension="qos", service="network")
+ def resource_setup(cls):
+ super(QosMinimumBandwidthRuleTestJSON, cls).resource_setup()
+
+ @test.idempotent_id('aa59b00b-3e9c-4787-92f8-93a5cdf5e378')
+ def test_rule_create(self):
+ policy = self.create_qos_policy(name='test-policy',
+ description='test policy',
+ shared=False)
+ rule = self.admin_client.create_minimum_bandwidth_rule(
+ policy_id=policy['id'],
+ direction=self.DIRECTION_EGRESS,
+ min_kbps=1138)[self.RULE_NAME]
+
+ # Test 'show rule'
+ retrieved_rule = self.admin_client.show_minimum_bandwidth_rule(
+ policy['id'], rule['id'])
+ retrieved_rule = retrieved_rule[self.RULE_NAME]
+ self.assertEqual(rule['id'], retrieved_rule['id'])
+ self.assertEqual(1138, retrieved_rule['min_kbps'])
+ self.assertEqual(self.DIRECTION_EGRESS, retrieved_rule['direction'])
+
+ # Test 'list rules'
+ rules = self.admin_client.list_minimum_bandwidth_rules(policy['id'])
+ rules = rules[self.RULES_NAME]
+ rules_ids = [r['id'] for r in rules]
+ self.assertIn(rule['id'], rules_ids)
+
+ # Test 'show policy'
+ retrieved_policy = self.admin_client.show_qos_policy(policy['id'])
+ policy_rules = retrieved_policy['policy']['rules']
+ self.assertEqual(1, len(policy_rules))
+ self.assertEqual(rule['id'], policy_rules[0]['id'])
+ self.assertEqual(qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH,
+ policy_rules[0]['type'])
+
+ @test.idempotent_id('266d9b87-e51c-48bd-9aa7-8269573621be')
+ def test_rule_create_fail_for_missing_min_kbps(self):
+ policy = self.create_qos_policy(name='test-policy',
+ description='test policy',
+ shared=False)
+ self.assertRaises(exceptions.BadRequest,
+ self.admin_client.create_minimum_bandwidth_rule,
+ policy_id=policy['id'],
+ direction=self.DIRECTION_EGRESS)
+
+ @test.idempotent_id('aa59b00b-ab01-4787-92f8-93a5cdf5e378')
+ def test_rule_create_fail_for_the_same_type(self):
+ policy = self.create_qos_policy(name='test-policy',
+ description='test policy',
+ shared=False)
+ self.admin_client.create_minimum_bandwidth_rule(
+ policy_id=policy['id'],
+ direction=self.DIRECTION_EGRESS, min_kbps=200)
+
+ self.assertRaises(exceptions.Conflict,
+ self.admin_client.create_minimum_bandwidth_rule,
+ policy_id=policy['id'],
+ direction=self.DIRECTION_EGRESS, min_kbps=201)
+
+ @test.idempotent_id('d6fce764-e511-4fa6-9f86-f4b41cf142cf')
+ def test_rule_create_fail_for_direction_ingress(self):
+ policy = self.create_qos_policy(name='test-policy',
+ description='test policy',
+ shared=False)
+ self.assertRaises(exceptions.BadRequest,
+ self.admin_client.create_minimum_bandwidth_rule,
+ policy_id=policy['id'],
+ direction=self.DIRECTION_INGRESS,
+ min_kbps=201)
+
+ @test.idempotent_id('a49a6988-2568-47d2-931e-2dbc858943b3')
+ def test_rule_update(self):
+ policy = self.create_qos_policy(name='test-policy',
+ description='test policy',
+ shared=False)
+ rule = self.admin_client.create_minimum_bandwidth_rule(
+ policy_id=policy['id'],
+ direction=self.DIRECTION_EGRESS,
+ min_kbps=300)[self.RULE_NAME]
+
+ self.admin_client.update_minimum_bandwidth_rule(policy['id'],
+ rule['id'], min_kbps=350, direction=self.DIRECTION_EGRESS)
+
+ retrieved_policy = self.admin_client.show_minimum_bandwidth_rule(
+ policy['id'], rule['id'])
+ retrieved_policy = retrieved_policy[self.RULE_NAME]
+ self.assertEqual(350, retrieved_policy['min_kbps'])
+ self.assertEqual(self.DIRECTION_EGRESS, retrieved_policy['direction'])
+
+ @test.idempotent_id('a7ee6efd-7b33-4a68-927d-275b4f8ba958')
+ def test_rule_delete(self):
+ policy = self.create_qos_policy(name='test-policy',
+ description='test policy',
+ shared=False)
+ rule = self.admin_client.create_minimum_bandwidth_rule(
+ policy['id'], self.DIRECTION_EGRESS, min_kbps=200)[self.RULE_NAME]
+
+ retrieved_policy = self.admin_client.show_minimum_bandwidth_rule(
+ policy['id'], rule['id'])
+ retrieved_policy = retrieved_policy[self.RULE_NAME]
+ self.assertEqual(rule['id'], retrieved_policy['id'])
+
+ self.admin_client.delete_minimum_bandwidth_rule(policy['id'],
+ rule['id'])
+ self.assertRaises(exceptions.NotFound,
+ self.admin_client.show_minimum_bandwidth_rule,
+ policy['id'], rule['id'])
+
+ @test.idempotent_id('a211222c-5808-46cb-a961-983bbab6b852')
+ def test_rule_create_rule_nonexistent_policy(self):
+ self.assertRaises(
+ exceptions.NotFound,
+ self.admin_client.create_minimum_bandwidth_rule,
+ 'policy', self.DIRECTION_EGRESS, min_kbps=200)
+
+ @test.idempotent_id('b4a2e7ad-786f-4927-a85a-e545a93bd274')
+ def test_rule_create_forbidden_for_regular_tenants(self):
+ self.assertRaises(
+ exceptions.Forbidden,
+ self.client.create_minimum_bandwidth_rule,
+ 'policy', self.DIRECTION_EGRESS, min_kbps=300)
+
+ @test.idempotent_id('de0bd0c2-54d9-4e29-85f1-cfb36ac3ebe2')
+ def test_get_rules_by_policy(self):
+ policy1 = self.create_qos_policy(name='test-policy1',
+ description='test policy1',
+ shared=False)
+ rule1 = self.admin_client.create_minimum_bandwidth_rule(
+ policy_id=policy1['id'],
+ direction=self.DIRECTION_EGRESS,
+ min_kbps=200)[self.RULE_NAME]
+
+ policy2 = self.create_qos_policy(name='test-policy2',
+ description='test policy2',
+ shared=False)
+ rule2 = self.admin_client.create_minimum_bandwidth_rule(
+ policy_id=policy2['id'],
+ direction=self.DIRECTION_EGRESS,
+ min_kbps=5000)[self.RULE_NAME]
+
+ # Test 'list rules'
+ rules = self.admin_client.list_minimum_bandwidth_rules(policy1['id'])
+ rules = rules[self.RULES_NAME]
+ rules_ids = [r['id'] for r in rules]
+ self.assertIn(rule1['id'], rules_ids)
+ self.assertNotIn(rule2['id'], rules_ids)
+
+
class QosSearchCriteriaTest(base.BaseSearchCriteriaTest,
base.BaseAdminNetworkTest):
diff --git a/neutron/tests/tempest/api/test_revisions.py b/neutron/tests/tempest/api/test_revisions.py
index 92f7866..a70b995 100644
--- a/neutron/tests/tempest/api/test_revisions.py
+++ b/neutron/tests/tempest/api/test_revisions.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import netaddr
+
from tempest import test
from neutron.tests.tempest.api import base
@@ -20,50 +22,66 @@
class TestRevisions(base.BaseAdminNetworkTest, bsg.BaseSecGroupTest):
@classmethod
- @test.requires_ext(extension="revisions", service="network")
+ @test.requires_ext(extension="standard-attr-revisions", service="network")
def skip_checks(cls):
super(TestRevisions, cls).skip_checks()
@test.idempotent_id('4a26a4be-9c53-483c-bc50-b53f1db10ac6')
def test_update_network_bumps_revision(self):
net = self.create_network()
- self.assertIn('revision', net)
+ self.assertIn('revision_number', net)
updated = self.client.update_network(net['id'], name='newnet')
- self.assertGreater(updated['network']['revision'], net['revision'])
+ self.assertGreater(updated['network']['revision_number'],
+ net['revision_number'])
@test.idempotent_id('cac7ecde-12d5-4331-9a03-420899dea077')
def test_update_port_bumps_revision(self):
net = self.create_network()
port = self.create_port(net)
- self.assertIn('revision', port)
+ self.assertIn('revision_number', port)
updated = self.client.update_port(port['id'], name='newport')
- self.assertGreater(updated['port']['revision'], port['revision'])
+ self.assertGreater(updated['port']['revision_number'],
+ port['revision_number'])
@test.idempotent_id('c1c4fa41-8e89-44d0-9bfc-409f3b66dc57')
def test_update_subnet_bumps_revision(self):
net = self.create_network()
subnet = self.create_subnet(net)
- self.assertIn('revision', subnet)
+ self.assertIn('revision_number', subnet)
updated = self.client.update_subnet(subnet['id'], name='newsub')
- self.assertGreater(updated['subnet']['revision'], subnet['revision'])
+ self.assertGreater(updated['subnet']['revision_number'],
+ subnet['revision_number'])
@test.idempotent_id('e8c5d7db-2b8d-4615-a476-6e537437c4f2')
def test_update_subnetpool_bumps_revision(self):
sp = self.create_subnetpool('subnetpool', default_prefixlen=24,
prefixes=['10.0.0.0/8'])
- self.assertIn('revision', sp)
+ self.assertIn('revision_number', sp)
updated = self.admin_client.update_subnetpool(sp['id'], name='sp2')
- self.assertGreater(updated['subnetpool']['revision'], sp['revision'])
+ self.assertGreater(updated['subnetpool']['revision_number'],
+ sp['revision_number'])
+
+ @test.idempotent_id('e8c5d7db-2b8d-4567-a326-6e123437c4d1')
+ def test_update_subnet_bumps_network_revision(self):
+ net = self.create_network()
+ subnet = self.create_subnet(net)
+ updated = self.client.show_network(net['id'])
+ self.assertGreater(updated['network']['revision_number'],
+ net['revision_number'])
+ self.client.delete_subnet(subnet['id'])
+ updated2 = self.client.show_network(net['id'])
+ self.assertGreater(updated2['network']['revision_number'],
+ updated['network']['revision_number'])
@test.idempotent_id('6c256f71-c929-4200-b3dc-4e1843506be5')
@test.requires_ext(extension="security-group", service="network")
def test_update_sg_group_bumps_revision(self):
sg, name = self._create_security_group()
- self.assertIn('revision', sg['security_group'])
+ self.assertIn('revision_number', sg['security_group'])
update_body = self.client.update_security_group(
sg['security_group']['id'], name='new_sg_name')
- self.assertGreater(update_body['security_group']['revision'],
- sg['security_group']['revision'])
+ self.assertGreater(update_body['security_group']['revision_number'],
+ sg['security_group']['revision_number'])
@test.idempotent_id('6489632f-8550-4453-a674-c98849742967')
@test.requires_ext(extension="security-group", service="network")
@@ -74,15 +92,11 @@
self.client.update_port(
port['id'], security_groups=[sg['security_group']['id']])
updated = self.client.show_port(port['id'])
- self.client.update_port(port['id'], security_groups=[])
- # TODO(kevinbenton): these extra shows after after the update are
- # to work around the fact that ML2 creates the result dict before
- # commit happens if the port is unbound. The update response should
- # be usable directly once that is fixed.
- updated2 = self.client.show_port(port['id'])
- self.assertGreater(updated['port']['revision'], port['revision'])
- self.assertGreater(updated2['port']['revision'],
- updated['port']['revision'])
+ updated2 = self.client.update_port(port['id'], security_groups=[])
+ self.assertGreater(updated['port']['revision_number'],
+ port['revision_number'])
+ self.assertGreater(updated2['port']['revision_number'],
+ updated['port']['revision_number'])
@test.idempotent_id('29c7ab2b-d1d8-425d-8cec-fcf632960f22')
@test.requires_ext(extension="security-group", service="network")
@@ -93,27 +107,138 @@
protocol='tcp', direction='ingress', ethertype=self.ethertype,
port_range_min=60, port_range_max=70)
updated = self.client.show_security_group(sg['security_group']['id'])
- self.assertGreater(updated['security_group']['revision'],
- sg['security_group']['revision'])
+ self.assertGreater(updated['security_group']['revision_number'],
+ sg['security_group']['revision_number'])
self.client.delete_security_group_rule(
rule['security_group_rule']['id'])
updated2 = self.client.show_security_group(sg['security_group']['id'])
- self.assertGreater(updated2['security_group']['revision'],
- updated['security_group']['revision'])
+ self.assertGreater(updated2['security_group']['revision_number'],
+ updated['security_group']['revision_number'])
+
+ @test.idempotent_id('db70c285-0365-4fac-9f55-2a0ad8cf55a8')
+ @test.requires_ext(extension="allowed-address-pairs", service="network")
+ def test_update_allowed_address_pairs_bumps_revision(self):
+ net = self.create_network()
+ port = self.create_port(net)
+ updated = self.client.update_port(
+ port['id'], allowed_address_pairs=[{'ip_address': '1.1.1.1/32'}])
+ self.assertGreater(updated['port']['revision_number'],
+ port['revision_number'])
+ updated2 = self.client.update_port(
+ port['id'], allowed_address_pairs=[])
+ self.assertGreater(updated2['port']['revision_number'],
+ updated['port']['revision_number'])
+
+ @test.idempotent_id('a21ec3b4-3569-4b77-bf29-4177edaa2df5')
+ @test.requires_ext(extension="extra_dhcp_opt", service="network")
+ def test_update_extra_dhcp_opt_bumps_revision(self):
+ net = self.create_network()
+ port = self.create_port(net)
+ opts = [{'opt_value': 'pxelinux.0', 'opt_name': 'bootfile-name'}]
+ updated = self.client.update_port(port['id'], extra_dhcp_opts=opts)
+ self.assertGreater(updated['port']['revision_number'],
+ port['revision_number'])
+ opts[0]['opt_value'] = 'pxelinux.77'
+ updated2 = self.client.update_port(
+ port['id'], extra_dhcp_opts=opts)
+ self.assertGreater(updated2['port']['revision_number'],
+ updated['port']['revision_number'])
+
+ @test.idempotent_id('40ba648f-f374-4c29-a5b7-489dd5a38a4e')
+ @test.requires_ext(extension="dns-integration", service="network")
+ def test_update_dns_domain_bumps_revision(self):
+ net = self.create_network(dns_domain='example.test.')
+ updated = self.client.update_network(net['id'], dns_domain='exa.test.')
+ self.assertGreater(updated['network']['revision_number'],
+ net['revision_number'])
+ port = self.create_port(net)
+ updated = self.client.update_port(port['id'], dns_name='port1')
+ if not updated['port']['dns_name']:
+ self.skipTest("Server does not have DNS domain configured.")
+ self.assertGreater(updated['port']['revision_number'],
+ port['revision_number'])
+ updated2 = self.client.update_port(port['id'], dns_name='')
+ self.assertGreater(updated2['port']['revision_number'],
+ updated['port']['revision_number'])
+
+ @test.idempotent_id('8482324f-cf59-4d73-b98e-d37119255300')
+ @test.requires_ext(extension="router", service="network")
+ @test.requires_ext(extension="extraroute", service="network")
+ def test_update_router_extra_routes_bumps_revision(self):
+ subnet = self.create_subnet(self.create_network())
+ subgateway = netaddr.IPAddress(subnet['gateway_ip'])
+ router = self.create_router(router_name='test')
+ self.create_router_interface(router['id'], subnet['id'])
+ router = self.client.show_router(router['id'])['router']
+ updated = self.client.update_router(
+ router['id'], routes=[{'destination': '2.0.0.0/24',
+ 'nexthop': str(subgateway + 1)}])
+ self.assertGreater(updated['router']['revision_number'],
+ router['revision_number'])
+ updated2 = self.client.update_router(router['id'], routes=[])
+ self.assertGreater(updated2['router']['revision_number'],
+ updated['router']['revision_number'])
+
+ @test.idempotent_id('6bd18702-e25a-4b4b-8c0c-680113533511')
+ @test.requires_ext(extension="subnet-service-types", service="network")
+ def test_update_subnet_service_types_bumps_revisions(self):
+ subnet = self.create_subnet(self.create_network())
+ updated = self.client.update_subnet(
+ subnet['id'], service_types=['compute:'])
+ self.assertGreater(updated['subnet']['revision_number'],
+ subnet['revision_number'])
+ updated2 = self.client.update_subnet(
+ subnet['id'], service_types=[])
+ self.assertGreater(updated2['subnet']['revision_number'],
+ updated['subnet']['revision_number'])
+
+ @test.idempotent_id('9c83105c-9973-45ff-9ca2-e66d64700abe')
+ @test.requires_ext(extension="port-security", service="network")
+ def test_update_port_security_bumps_revisions(self):
+ net = self.create_network(port_security_enabled=False)
+ updated = self.client.update_network(net['id'],
+ port_security_enabled=True)
+ self.assertGreater(updated['network']['revision_number'],
+ net['revision_number'])
+ updated2 = self.client.update_network(net['id'],
+ port_security_enabled=False)
+ self.assertGreater(updated2['network']['revision_number'],
+ updated['network']['revision_number'])
+ port = self.create_port(net, port_security_enabled=False)
+ updated = self.client.update_port(port['id'],
+ port_security_enabled=True)
+ self.assertGreater(updated['port']['revision_number'],
+ port['revision_number'])
+ updated2 = self.client.update_port(port['id'],
+ port_security_enabled=False)
+ self.assertGreater(updated2['port']['revision_number'],
+ updated['port']['revision_number'])
+
+ @test.idempotent_id('68d5ac3a-11a1-4847-8e2e-5843c043d89b')
+ @test.requires_ext(extension="binding", service="network")
+ def test_portbinding_bumps_revision(self):
+ port = self.create_port(self.create_network())
+ port = self.admin_client.update_port(
+ port['id'], **{'binding:host_id': 'badhost1'})['port']
+ updated = self.admin_client.update_port(
+ port['id'], **{'binding:host_id': 'badhost2'})['port']
+ self.assertGreater(updated['revision_number'],
+ port['revision_number'])
@test.idempotent_id('4a37bde9-1975-47e0-9b8c-2c9ca36415b0')
@test.requires_ext(extension="router", service="network")
def test_update_router_bumps_revision(self):
subnet = self.create_subnet(self.create_network())
router = self.create_router(router_name='test')
- self.assertIn('revision', router)
- rev1 = router['revision']
+ self.assertIn('revision_number', router)
+ rev1 = router['revision_number']
router = self.client.update_router(router['id'],
name='test2')['router']
- self.assertGreater(router['revision'], rev1)
+ self.assertGreater(router['revision_number'], rev1)
self.create_router_interface(router['id'], subnet['id'])
updated = self.client.show_router(router['id'])['router']
- self.assertGreater(updated['revision'], router['revision'])
+ self.assertGreater(updated['revision_number'],
+ router['revision_number'])
@test.idempotent_id('9de71ebc-f5df-4cd0-80bc-60299fce3ce9')
@test.requires_ext(extension="router", service="network")
@@ -131,6 +256,9 @@
port_id=port['id'],
description='d1'
)['floatingip']
- self.assertIn('revision', body)
+ self.assertIn('revision_number', body)
b2 = self.client.update_floatingip(body['id'], description='d2')
- self.assertGreater(b2['floatingip']['revision'], body['revision'])
+ self.assertGreater(b2['floatingip']['revision_number'],
+ body['revision_number'])
+ # disassociate
+ self.client.update_floatingip(b2['floatingip']['id'], port_id=None)
diff --git a/neutron/tests/tempest/api/test_security_groups_negative.py b/neutron/tests/tempest/api/test_security_groups_negative.py
index 68b5fd0..43bc88c 100644
--- a/neutron/tests/tempest/api/test_security_groups_negative.py
+++ b/neutron/tests/tempest/api/test_security_groups_negative.py
@@ -17,9 +17,6 @@
from tempest import test
from neutron.tests.tempest.api import base_security_groups as base
-from neutron.tests.tempest import config
-
-CONF = config.CONF
class NegativeSecGroupTest(base.BaseSecGroupTest):
diff --git a/neutron/tests/tempest/api/test_subnetpools.py b/neutron/tests/tempest/api/test_subnetpools.py
index 5bd222f..e8ec954 100644
--- a/neutron/tests/tempest/api/test_subnetpools.py
+++ b/neutron/tests/tempest/api/test_subnetpools.py
@@ -110,13 +110,15 @@
body = self._create_subnetpool(description='d1')
self.assertEqual('d1', body['description'])
sub_id = body['id']
- body = filter(lambda x: x['id'] == sub_id,
- self.client.list_subnetpools()['subnetpools'])[0]
+ subnet_pools = [x for x in
+ self.client.list_subnetpools()['subnetpools'] if x['id'] == sub_id]
+ body = subnet_pools[0]
self.assertEqual('d1', body['description'])
body = self.client.update_subnetpool(sub_id, description='d2')
self.assertEqual('d2', body['subnetpool']['description'])
- body = filter(lambda x: x['id'] == sub_id,
- self.client.list_subnetpools()['subnetpools'])[0]
+ subnet_pools = [x for x in
+ self.client.list_subnetpools()['subnetpools'] if x['id'] == sub_id]
+ body = subnet_pools[0]
self.assertEqual('d2', body['description'])
@test.idempotent_id('741d08c2-1e3f-42be-99c7-0ea93c5b728c')
@@ -390,3 +392,7 @@
@test.idempotent_id('82a13efc-c18f-4249-b8ec-cec7cf26fbd6')
def test_list_no_pagination_limit_0(self):
self._test_list_no_pagination_limit_0()
+
+ @test.idempotent_id('27feb3f8-40f4-4e50-8cd2-7d0096a98682')
+ def test_list_validation_filters(self):
+ self._test_list_validation_filters()
diff --git a/neutron/tests/tempest/api/test_subnets.py b/neutron/tests/tempest/api/test_subnets.py
index a3a2e00..4e63a3a 100644
--- a/neutron/tests/tempest/api/test_subnets.py
+++ b/neutron/tests/tempest/api/test_subnets.py
@@ -63,3 +63,7 @@
@test.idempotent_id('d851937c-9821-4b46-9d18-43e9077ecac0')
def test_list_no_pagination_limit_0(self):
self._test_list_no_pagination_limit_0()
+
+ @test.idempotent_id('c0f9280b-9d81-4728-a967-6be22659d4c8')
+ def test_list_validation_filters(self):
+ self._test_list_validation_filters()
diff --git a/neutron/tests/tempest/api/test_timestamp.py b/neutron/tests/tempest/api/test_timestamp.py
index 6c36cf6..d3a2361 100644
--- a/neutron/tests/tempest/api/test_timestamp.py
+++ b/neutron/tests/tempest/api/test_timestamp.py
@@ -16,6 +16,11 @@
from tempest import test
from neutron.tests.tempest.api import base
+from neutron.tests.tempest.api import base_routers
+from neutron.tests.tempest.api import base_security_groups
+from neutron.tests.tempest import config
+
+CONF = config.CONF
class TestTimeStamp(base.BaseAdminNetworkTest):
@@ -29,7 +34,7 @@
larger_prefix = '10.11.0.0/16'
@classmethod
- @test.requires_ext(extension="timestamp_core", service="network")
+ @test.requires_ext(extension="standard-attr-timestamp", service="network")
def skip_checks(cls):
super(TestTimeStamp, cls).skip_checks()
@@ -174,3 +179,159 @@
# verify the timestamp from creation and showed is same
self.assertEqual(sp['created_at'], show_sp['created_at'])
self.assertEqual(sp['updated_at'], show_sp['updated_at'])
+
+
+class TestTimeStampWithL3(base_routers.BaseRouterTest):
+ @classmethod
+ def skip_checks(cls):
+ super(TestTimeStampWithL3, cls).skip_checks()
+
+ if not test.is_extension_enabled('standard-attr-timestamp', 'network'):
+ raise cls.skipException("standard-attr-timestamp extension not "
+ "enabled")
+
+ @classmethod
+ def resource_setup(cls):
+ super(TestTimeStampWithL3, cls).resource_setup()
+ cls.ext_net_id = CONF.network.public_network_id
+
+ @test.idempotent_id('433ba770-b310-4da9-5d42-733217a1c7b1')
+ def test_create_router_with_timestamp(self):
+ router = self.create_router(router_name='test')
+ # Verifies body contains timestamp fields
+ self.assertIsNotNone(router['created_at'])
+ self.assertIsNotNone(router['updated_at'])
+
+ @test.idempotent_id('4a65417a-c11c-4b4d-a351-af01abcf57c6')
+ def test_update_router_with_timestamp(self):
+ router = self.create_router(router_name='test')
+ origin_updated_at = router['updated_at']
+ update_body = {'name': router['name'] + 'new'}
+ body = self.client.update_router(router['id'], **update_body)
+ updated_router = body['router']
+ new_updated_at = updated_router['updated_at']
+ self.assertEqual(router['created_at'], updated_router['created_at'])
+ # Verify that origin_updated_at is not same with new_updated_at
+ self.assertIsNot(origin_updated_at, new_updated_at)
+
+ @test.idempotent_id('1ab50ac2-7cbd-4a17-b23e-a9e36cfa4ec2')
+ def test_show_router_attribute_with_timestamp(self):
+ router = self.create_router(router_name='test')
+ body = self.client.show_router(router['id'])
+ show_router = body['router']
+ # verify the timestamp from creation and showed is same
+ self.assertEqual(router['created_at'],
+ show_router['created_at'])
+ self.assertEqual(router['updated_at'],
+ show_router['updated_at'])
+
+ @test.idempotent_id('8ae55186-464f-4b87-1c9f-eb2765ee81ac')
+ def test_create_floatingip_with_timestamp(self):
+ fip = self.create_floatingip(self.ext_net_id)
+ # Verifies body contains timestamp fields
+ self.assertIsNotNone(fip['created_at'])
+ self.assertIsNotNone(fip['updated_at'])
+
+ @test.idempotent_id('a3ac215a-61ac-13f9-9d3c-57c51f11afa1')
+ def test_update_floatingip_with_timestamp(self):
+ fip = self.create_floatingip(self.ext_net_id)
+ origin_updated_at = fip['updated_at']
+ update_body = {'description': 'new'}
+ body = self.client.update_floatingip(fip['id'], **update_body)
+ updated_fip = body['floatingip']
+ new_updated_at = updated_fip['updated_at']
+ self.assertEqual(fip['created_at'], updated_fip['created_at'])
+ # Verify that origin_updated_at is not same with new_updated_at
+ self.assertIsNot(origin_updated_at, new_updated_at)
+
+ @test.idempotent_id('32a6a086-e1ef-413b-b13a-0cfe13ef051e')
+ def test_show_floatingip_attribute_with_timestamp(self):
+ fip = self.create_floatingip(self.ext_net_id)
+ body = self.client.show_floatingip(fip['id'])
+ show_fip = body['floatingip']
+ # verify the timestamp from creation and showed is same
+ self.assertEqual(fip['created_at'],
+ show_fip['created_at'])
+ self.assertEqual(fip['updated_at'],
+ show_fip['updated_at'])
+
+
+class TestTimeStampWithSecurityGroup(base_security_groups.BaseSecGroupTest):
+ @classmethod
+ def skip_checks(cls):
+ super(TestTimeStampWithSecurityGroup, cls).skip_checks()
+
+ if not test.is_extension_enabled('standard-attr-timestamp', 'network'):
+ raise cls.skipException("standard-attr-timestamp extension not "
+ "enabled")
+
+ @classmethod
+ def resource_setup(cls):
+ super(TestTimeStampWithSecurityGroup, cls).resource_setup()
+ cls.ext_net_id = CONF.network.public_network_id
+
+ @test.idempotent_id('a3150a7b-d31a-423a-abf3-45e71c97cbac')
+ def test_create_sg_with_timestamp(self):
+ sg, _ = self._create_security_group()
+ # Verifies body contains timestamp fields
+ self.assertIsNotNone(sg['security_group']['created_at'])
+ self.assertIsNotNone(sg['security_group']['updated_at'])
+
+ @test.idempotent_id('432ae0d3-32b4-413e-a9b3-091ac76da31b')
+ def test_update_sg_with_timestamp(self):
+ sgc, _ = self._create_security_group()
+ sg = sgc['security_group']
+ origin_updated_at = sg['updated_at']
+ update_body = {'name': sg['name'] + 'new'}
+ body = self.client.update_security_group(sg['id'], **update_body)
+ updated_sg = body['security_group']
+ new_updated_at = updated_sg['updated_at']
+ self.assertEqual(sg['created_at'], updated_sg['created_at'])
+ # Verify that origin_updated_at is not same with new_updated_at
+ self.assertIsNot(origin_updated_at, new_updated_at)
+
+ @test.idempotent_id('521e6723-43d6-12a6-8c3d-f5042ad9fc32')
+ def test_show_sg_attribute_with_timestamp(self):
+ sg, _ = self._create_security_group()
+ body = self.client.show_security_group(sg['security_group']['id'])
+ show_sg = body['security_group']
+ # verify the timestamp from creation and showed is same
+ self.assertEqual(sg['security_group']['created_at'],
+ show_sg['created_at'])
+ self.assertEqual(sg['security_group']['updated_at'],
+ show_sg['updated_at'])
+
+ def _prepare_sgrule_test(self):
+ sg, _ = self._create_security_group()
+ sg_id = sg['security_group']['id']
+ direction = 'ingress'
+ protocol = 'tcp'
+ port_range_min = 77
+ port_range_max = 77
+ rule_create_body = self.client.create_security_group_rule(
+ security_group_id=sg_id,
+ direction=direction,
+ ethertype=self.ethertype,
+ protocol=protocol,
+ port_range_min=port_range_min,
+ port_range_max=port_range_max,
+ remote_group_id=None,
+ remote_ip_prefix=None
+ )
+ return rule_create_body['security_group_rule']
+
+ @test.idempotent_id('83e8bd32-43e0-a3f0-1af3-12a5733c653e')
+ def test_create_sgrule_with_timestamp(self):
+ sgrule = self._prepare_sgrule_test()
+ # Verifies body contains timestamp fields
+ self.assertIsNotNone(sgrule['created_at'])
+ self.assertIsNotNone(sgrule['updated_at'])
+
+ @test.idempotent_id('143da0e6-ba17-43ad-b3d7-03aa759c3cb4')
+ def test_show_sgrule_attribute_with_timestamp(self):
+ sgrule = self._prepare_sgrule_test()
+ body = self.client.show_security_group_rule(sgrule['id'])
+ show_sgrule = body['security_group_rule']
+ # verify the timestamp from creation and showed is same
+ self.assertEqual(sgrule['created_at'], show_sgrule['created_at'])
+ self.assertEqual(sgrule['updated_at'], show_sgrule['updated_at'])
diff --git a/neutron/tests/tempest/api/test_trunk.py b/neutron/tests/tempest/api/test_trunk.py
index c22c334..3dc8f0d 100644
--- a/neutron/tests/tempest/api/test_trunk.py
+++ b/neutron/tests/tempest/api/test_trunk.py
@@ -12,11 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
from neutron.tests.tempest.api import base
+from neutron.tests.tempest import config
def trunks_cleanup(client, trunks):
@@ -65,53 +67,73 @@
self.trunks.append(trunk['trunk'])
return trunk
+ def _show_trunk(self, trunk_id):
+ return self.client.show_trunk(trunk_id)
+
+ def _list_trunks(self):
+ return self.client.list_trunks()
+
class TrunkTestJSON(TrunkTestJSONBase):
+ def _test_create_trunk(self, subports):
+ trunk = self._create_trunk_with_network_and_parent(subports)
+ observed_trunk = self._show_trunk(trunk['trunk']['id'])
+ self.assertEqual(trunk, observed_trunk)
+
@test.idempotent_id('e1a6355c-4768-41f3-9bf8-0f1d192bd501')
def test_create_trunk_empty_subports_list(self):
- trunk = self._create_trunk_with_network_and_parent([])
- observed_trunk = self.client.show_trunk(trunk['trunk']['id'])
- self.assertEqual(trunk, observed_trunk)
+ self._test_create_trunk([])
@test.idempotent_id('382dfa39-ca03-4bd3-9a1c-91e36d2e3796')
def test_create_trunk_subports_not_specified(self):
- trunk = self._create_trunk_with_network_and_parent(None)
- observed_trunk = self.client.show_trunk(trunk['trunk']['id'])
- self.assertEqual(trunk, observed_trunk)
+ self._test_create_trunk(None)
@test.idempotent_id('7de46c22-e2b6-4959-ac5a-0e624632ab32')
def test_create_show_delete_trunk(self):
trunk = self._create_trunk_with_network_and_parent(None)
trunk_id = trunk['trunk']['id']
parent_port_id = trunk['trunk']['port_id']
- res = self.client.show_trunk(trunk_id)
+ res = self._show_trunk(trunk_id)
self.assertEqual(trunk_id, res['trunk']['id'])
self.assertEqual(parent_port_id, res['trunk']['port_id'])
self.client.delete_trunk(trunk_id)
- self.assertRaises(lib_exc.NotFound, self.client.show_trunk, trunk_id)
+ self.assertRaises(lib_exc.NotFound, self._show_trunk, trunk_id)
@test.idempotent_id('4ce46c22-a2b6-4659-bc5a-0ef2463cab32')
def test_create_update_trunk(self):
trunk = self._create_trunk_with_network_and_parent(None)
+ self.assertEqual(1, trunk['trunk']['revision_number'])
trunk_id = trunk['trunk']['id']
- res = self.client.show_trunk(trunk_id)
+ res = self._show_trunk(trunk_id)
self.assertTrue(res['trunk']['admin_state_up'])
+ self.assertEqual(1, res['trunk']['revision_number'])
self.assertEqual("", res['trunk']['name'])
+ self.assertEqual("", res['trunk']['description'])
res = self.client.update_trunk(
trunk_id, name='foo', admin_state_up=False)
self.assertFalse(res['trunk']['admin_state_up'])
self.assertEqual("foo", res['trunk']['name'])
+ self.assertGreater(res['trunk']['revision_number'], 1)
# enable the trunk so that it can be managed
self.client.update_trunk(trunk_id, admin_state_up=True)
+ @test.idempotent_id('5ff46c22-a2b6-5559-bc5a-0ef2463cab32')
+ def test_create_update_trunk_with_description(self):
+ trunk = self._create_trunk_with_network_and_parent(
+ None, description="foo description")
+ trunk_id = trunk['trunk']['id']
+ self.assertEqual("foo description", trunk['trunk']['description'])
+ trunk = self.client.update_trunk(trunk_id, description='')
+ self.assertEqual('', trunk['trunk']['description'])
+
@test.idempotent_id('73365f73-bed6-42cd-960b-ec04e0c99d85')
def test_list_trunks(self):
trunk1 = self._create_trunk_with_network_and_parent(None)
trunk2 = self._create_trunk_with_network_and_parent(None)
expected_trunks = {trunk1['trunk']['id']: trunk1['trunk'],
trunk2['trunk']['id']: trunk2['trunk']}
- trunk_list = self.client.list_trunks()['trunks']
+ trunk_list = self._list_trunks()['trunks']
matched_trunks = [x for x in trunk_list if x['id'] in expected_trunks]
self.assertEqual(2, len(matched_trunks))
for trunk in matched_trunks:
@@ -126,7 +148,7 @@
'segmentation_type': 'vlan',
'segmentation_id': 2}]
self.client.add_subports(trunk['trunk']['id'], subports)
- trunk = self.client.show_trunk(trunk['trunk']['id'])
+ trunk = self._show_trunk(trunk['trunk']['id'])
observed_subports = trunk['trunk']['sub_ports']
self.assertEqual(1, len(observed_subports))
created_subport = observed_subports[0]
@@ -168,7 +190,7 @@
self.assertEqual(expected_subport, res['sub_ports'][0])
# Validate the results of a subport list
- trunk = self.client.show_trunk(trunk['trunk']['id'])
+ trunk = self._show_trunk(trunk['trunk']['id'])
observed_subports = trunk['trunk']['sub_ports']
self.assertEqual(1, len(observed_subports))
self.assertEqual(expected_subport, observed_subports[0])
@@ -186,6 +208,85 @@
self.assertEqual(1, len(observed_subports))
+class TrunkTestMtusJSONBase(TrunkTestJSONBase):
+
+ required_extensions = ['provider', 'trunk']
+
+ @classmethod
+ def skip_checks(cls):
+ super(TrunkTestMtusJSONBase, cls).skip_checks()
+ for ext in cls.required_extensions:
+ if not test.is_extension_enabled(ext, 'network'):
+ msg = "%s extension not enabled." % ext
+ raise cls.skipException(msg)
+
+ if any(t
+ not in config.CONF.neutron_plugin_options.available_type_drivers
+ for t in ['gre', 'vxlan']):
+ msg = "Either vxlan or gre type driver not enabled."
+ raise cls.skipException(msg)
+
+ def setUp(self):
+ super(TrunkTestMtusJSONBase, self).setUp()
+
+ # VXLAN autocomputed MTU (1450) is smaller than that of GRE (1458)
+ vxlan_kwargs = {'network_name': data_utils.rand_name('vxlan-net-'),
+ 'provider:network_type': 'vxlan'}
+ self.smaller_mtu_net = self.create_shared_network(**vxlan_kwargs)
+
+ gre_kwargs = {'network_name': data_utils.rand_name('gre-net-'),
+ 'provider:network_type': 'gre'}
+ self.larger_mtu_net = self.create_shared_network(**gre_kwargs)
+
+ self.smaller_mtu_port = self.create_port(self.smaller_mtu_net)
+ self.smaller_mtu_port_2 = self.create_port(self.smaller_mtu_net)
+ self.larger_mtu_port = self.create_port(self.larger_mtu_net)
+
+
+class TrunkTestMtusJSON(TrunkTestMtusJSONBase):
+
+ @test.idempotent_id('0f05d98e-41f5-4629-ac29-9aee269c9602')
+ def test_create_trunk_with_mtu_greater_than_subport(self):
+ subports = [{'port_id': self.smaller_mtu_port['id'],
+ 'segmentation_type': 'vlan',
+ 'segmentation_id': 2}]
+
+ trunk = self.client.create_trunk(self.larger_mtu_port['id'], subports)
+ self.trunks.append(trunk['trunk'])
+
+ @test.idempotent_id('2004c5c6-e557-4c43-8100-c820ad4953e8')
+ def test_add_subport_with_mtu_smaller_than_trunk(self):
+ subports = [{'port_id': self.smaller_mtu_port['id'],
+ 'segmentation_type': 'vlan',
+ 'segmentation_id': 2}]
+
+ trunk = self.client.create_trunk(self.larger_mtu_port['id'], None)
+ self.trunks.append(trunk['trunk'])
+
+ self.client.add_subports(trunk['trunk']['id'], subports)
+
+ @test.idempotent_id('22725101-f4bc-4e00-84ec-4e02cd7e0500')
+ def test_create_trunk_with_mtu_equal_to_subport(self):
+ subports = [{'port_id': self.smaller_mtu_port['id'],
+ 'segmentation_type': 'vlan',
+ 'segmentation_id': 2}]
+
+ trunk = self.client.create_trunk(self.smaller_mtu_port_2['id'],
+ subports)
+ self.trunks.append(trunk['trunk'])
+
+ @test.idempotent_id('175b05ae-66ad-44c7-857a-a12d16f1058f')
+ def test_add_subport_with_mtu_equal_to_trunk(self):
+ subports = [{'port_id': self.smaller_mtu_port['id'],
+ 'segmentation_type': 'vlan',
+ 'segmentation_id': 2}]
+
+ trunk = self.client.create_trunk(self.smaller_mtu_port_2['id'], None)
+ self.trunks.append(trunk['trunk'])
+
+ self.client.add_subports(trunk['trunk']['id'], subports)
+
+
class TrunksSearchCriteriaTest(base.BaseSearchCriteriaTest):
resource = 'trunk'
diff --git a/neutron/tests/tempest/api/test_trunk_details.py b/neutron/tests/tempest/api/test_trunk_details.py
index 4b7ec28..0c91c45 100644
--- a/neutron/tests/tempest/api/test_trunk_details.py
+++ b/neutron/tests/tempest/api/test_trunk_details.py
@@ -35,15 +35,16 @@
@test.idempotent_id('544bcaf2-86fb-4930-93ab-ece1c3cc33df')
def test_port_resource_trunk_details_with_subport(self):
subport_network = self.create_network()
- parent_port = self.create_port(subport_network)
- subport_data = {'port_id': parent_port['id'],
+ subport = self.create_port(subport_network)
+ subport_data = {'port_id': subport['id'],
'segmentation_type': 'vlan',
'segmentation_id': 2}
trunk = self._create_trunk_with_network_and_parent([subport_data])
- port = self.client.show_port(trunk['trunk']['port_id'])
+ subport_data['mac_address'] = subport['mac_address']
+ parent_port = self.client.show_port(trunk['trunk']['port_id'])
expected_trunk_details = {'sub_ports': [subport_data],
'trunk_id': trunk['trunk']['id']}
- observed_trunk_details = port['port'].get('trunk_details')
+ observed_trunk_details = parent_port['port'].get('trunk_details')
self.assertIsNotNone(observed_trunk_details)
self.assertEqual(expected_trunk_details,
observed_trunk_details)
diff --git a/neutron/tests/tempest/api/test_trunk_negative.py b/neutron/tests/tempest/api/test_trunk_negative.py
index 654b497..b26dd75 100644
--- a/neutron/tests/tempest/api/test_trunk_negative.py
+++ b/neutron/tests/tempest/api/test_trunk_negative.py
@@ -15,6 +15,7 @@
from oslo_utils import uuidutils
from tempest.lib import exceptions as lib_exc
from tempest import test
+import testtools
from neutron.tests.tempest.api import test_trunk
@@ -235,3 +236,35 @@
self._create_trunk_with_network_and_parent(subports)
self.assertRaises(lib_exc.Conflict, self.client.delete_port,
port['id'])
+
+
+class TrunkTestMtusJSON(test_trunk.TrunkTestMtusJSONBase):
+
+ required_extensions = (
+ ['net-mtu'] + test_trunk.TrunkTestMtusJSONBase.required_extensions)
+
+ @test.attr(type='negative')
+ @test.idempotent_id('228380ef-1b7a-495e-b759-5b1f08e3e858')
+ def test_create_trunk_with_mtu_smaller_than_subport(self):
+ subports = [{'port_id': self.larger_mtu_port['id'],
+ 'segmentation_type': 'vlan',
+ 'segmentation_id': 2}]
+
+ with testtools.ExpectedException(lib_exc.Conflict):
+ trunk = self.client.create_trunk(self.smaller_mtu_port['id'],
+ subports)
+ self.trunks.append(trunk['trunk'])
+
+ @test.attr(type='negative')
+ @test.idempotent_id('3b32bf77-8002-403e-ad01-6f4cf018daa5')
+ def test_add_subport_with_mtu_greater_than_trunk(self):
+ subports = [{'port_id': self.larger_mtu_port['id'],
+ 'segmentation_type': 'vlan',
+ 'segmentation_id': 2}]
+
+ trunk = self.client.create_trunk(self.smaller_mtu_port['id'], None)
+ self.trunks.append(trunk['trunk'])
+
+ self.assertRaises(lib_exc.Conflict,
+ self.client.add_subports,
+ trunk['trunk']['id'], subports)
diff --git a/neutron/tests/tempest/config.py b/neutron/tests/tempest/config.py
index f50fa39..479d909 100644
--- a/neutron/tests/tempest/config.py
+++ b/neutron/tests/tempest/config.py
@@ -22,7 +22,12 @@
cfg.BoolOpt('specify_floating_ip_address_available',
default=True,
help='Allow passing an IP Address of the floating ip when '
- 'creating the floating ip')]
+ 'creating the floating ip'),
+ cfg.ListOpt('available_type_drivers',
+ default=[],
+ help='List of network types available to neutron, '
+ 'e.g. vxlan,vlan,gre.'),
+]
# TODO(amuller): Redo configuration options registration as part of the planned
# transition to the Tempest plugin architecture
diff --git a/neutron/tests/tempest/scenario/test_trunk.py b/neutron/tests/tempest/scenario/test_trunk.py
new file mode 100644
index 0000000..30d6022
--- /dev/null
+++ b/neutron/tests/tempest/scenario/test_trunk.py
@@ -0,0 +1,160 @@
+# All Rights Reserved.
+#
+# 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_log import log as logging
+from tempest.common import waiters
+from tempest import test
+
+from neutron.common import utils
+from neutron.tests.tempest import config
+from neutron.tests.tempest.scenario import base
+from neutron.tests.tempest.scenario import constants
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+
+class TrunkTest(base.BaseTempestTestCase):
+ credentials = ['primary']
+ force_tenant_isolation = False
+
+ @classmethod
+ @test.requires_ext(extension="trunk", service="network")
+ def resource_setup(cls):
+ super(TrunkTest, cls).resource_setup()
+ # setup basic topology for servers we can log into
+ cls.network = cls.create_network()
+ cls.subnet = cls.create_subnet(cls.network)
+ cls.create_router_and_interface(cls.subnet['id'])
+ cls.keypair = cls.create_keypair()
+ cls.create_loginable_secgroup_rule()
+
+ def _create_server_with_trunk_port(self):
+ port = self.create_port(self.network)
+ trunk = self.client.create_trunk(port['id'], subports=[])['trunk']
+ fip = self.create_and_associate_floatingip(port['id'])
+ server = self.create_server(
+ flavor_ref=CONF.compute.flavor_ref,
+ image_ref=CONF.compute.image_ref,
+ key_name=self.keypair['name'],
+ networks=[{'port': port['id']}])['server']
+ self.addCleanup(self._detach_and_delete_trunk, server, trunk)
+ return {'port': port, 'trunk': trunk, 'fip': fip,
+ 'server': server}
+
+ def _detach_and_delete_trunk(self, server, trunk):
+ # we have to detach the interface from the server before
+ # the trunk can be deleted.
+ self.manager.compute.InterfacesClient().delete_interface(
+ server['id'], trunk['port_id'])
+
+ def is_port_detached():
+ p = self.client.show_port(trunk['port_id'])['port']
+ return p['device_id'] == ''
+ utils.wait_until_true(is_port_detached)
+ self.client.delete_trunk(trunk['id'])
+
+ def _is_port_down(self, port_id):
+ p = self.client.show_port(port_id)['port']
+ return p['status'] == 'DOWN'
+
+ def _is_port_active(self, port_id):
+ p = self.client.show_port(port_id)['port']
+ return p['status'] == 'ACTIVE'
+
+ def _is_trunk_active(self, trunk_id):
+ t = self.client.show_trunk(trunk_id)['trunk']
+ return t['status'] == 'ACTIVE'
+
+ @test.idempotent_id('bb13fe28-f152-4000-8131-37890a40c79e')
+ def test_trunk_subport_lifecycle(self):
+ """Test trunk creation and subport transition to ACTIVE status.
+
+ This is a basic test for the trunk extension to ensure that we
+ can create a trunk, attach it to a server, add/remove subports,
+ while ensuring the status transitions as appropriate.
+
+ This test does not assert any dataplane behavior for the subports.
+ It's just a high-level check to ensure the agents claim to have
+ wired the port correctly and that the trunk port itself maintains
+ connectivity.
+ """
+ server1 = self._create_server_with_trunk_port()
+ server2 = self._create_server_with_trunk_port()
+ for server in (server1, server2):
+ waiters.wait_for_server_status(self.manager.servers_client,
+ server['server']['id'],
+ constants.SERVER_STATUS_ACTIVE)
+ self.check_connectivity(server['fip']['floating_ip_address'],
+ CONF.validation.image_ssh_user,
+ self.keypair['private_key'])
+ trunk1_id, trunk2_id = server1['trunk']['id'], server2['trunk']['id']
+ # trunks should transition to ACTIVE without any subports
+ utils.wait_until_true(
+ lambda: self._is_trunk_active(trunk1_id),
+ exception=RuntimeError("Timed out waiting for trunk %s to "
+ "transition to ACTIVE." % trunk1_id))
+ utils.wait_until_true(
+ lambda: self._is_trunk_active(trunk2_id),
+ exception=RuntimeError("Timed out waiting for trunk %s to "
+ "transition to ACTIVE." % trunk2_id))
+ # create a few more networks and ports for subports
+ subports = [{'port_id': self.create_port(self.create_network())['id'],
+ 'segmentation_type': 'vlan', 'segmentation_id': seg_id}
+ for seg_id in range(3, 7)]
+ # add all subports to server1
+ self.client.add_subports(trunk1_id, subports)
+ # ensure trunk transitions to ACTIVE
+ utils.wait_until_true(
+ lambda: self._is_trunk_active(trunk1_id),
+ exception=RuntimeError("Timed out waiting for trunk %s to "
+ "transition to ACTIVE." % trunk1_id))
+ # ensure all underlying subports transitioned to ACTIVE
+ for s in subports:
+ utils.wait_until_true(lambda: self._is_port_active(s['port_id']))
+ # ensure main dataplane wasn't interrupted
+ self.check_connectivity(server1['fip']['floating_ip_address'],
+ CONF.validation.image_ssh_user,
+ self.keypair['private_key'])
+ # move subports over to other server
+ self.client.remove_subports(trunk1_id, subports)
+ # ensure all subports go down
+ for s in subports:
+ utils.wait_until_true(
+ lambda: self._is_port_down(s['port_id']),
+ exception=RuntimeError("Timed out waiting for subport %s to "
+ "transition to DOWN." % s['port_id']))
+ self.client.add_subports(trunk2_id, subports)
+ # wait for both trunks to go back to ACTIVE
+ utils.wait_until_true(
+ lambda: self._is_trunk_active(trunk1_id),
+ exception=RuntimeError("Timed out waiting for trunk %s to "
+ "transition to ACTIVE." % trunk1_id))
+ utils.wait_until_true(
+ lambda: self._is_trunk_active(trunk2_id),
+ exception=RuntimeError("Timed out waiting for trunk %s to "
+ "transition to ACTIVE." % trunk2_id))
+ # ensure subports come up on other trunk
+ for s in subports:
+ utils.wait_until_true(
+ lambda: self._is_port_active(s['port_id']),
+ exception=RuntimeError("Timed out waiting for subport %s to "
+ "transition to ACTIVE." % s['port_id']))
+ # final connectivity check
+ self.check_connectivity(server1['fip']['floating_ip_address'],
+ CONF.validation.image_ssh_user,
+ self.keypair['private_key'])
+ self.check_connectivity(server2['fip']['floating_ip_address'],
+ CONF.validation.image_ssh_user,
+ self.keypair['private_key'])
diff --git a/neutron/tests/tempest/services/network/json/network_client.py b/neutron/tests/tempest/services/network/json/network_client.py
index 9c8cb05..3b1a9a8 100644
--- a/neutron/tests/tempest/services/network/json/network_client.py
+++ b/neutron/tests/tempest/services/network/json/network_client.py
@@ -55,6 +55,7 @@
'metering_label_rules': 'metering',
'policies': 'qos',
'bandwidth_limit_rules': 'qos',
+ 'minimum_bandwidth_rules': 'qos',
'rule_types': 'qos',
'rbac-policies': '',
}
@@ -652,6 +653,53 @@
self.expected_success(204, resp.status)
return service_client.ResponseBody(resp, body)
+ def create_minimum_bandwidth_rule(self, policy_id, direction,
+ min_kbps=None):
+ uri = '%s/qos/policies/%s/minimum_bandwidth_rules' % (
+ self.uri_prefix, policy_id)
+ data = {
+ 'direction': direction,
+ }
+ if min_kbps is not None:
+ data['min_kbps'] = min_kbps
+ post_data = self.serialize({'minimum_bandwidth_rule': data})
+ resp, body = self.post(uri, post_data)
+ self.expected_success(201, resp.status)
+ body = jsonutils.loads(body)
+ return service_client.ResponseBody(resp, body)
+
+ def list_minimum_bandwidth_rules(self, policy_id):
+ uri = '%s/qos/policies/%s/minimum_bandwidth_rules' % (
+ self.uri_prefix, policy_id)
+ resp, body = self.get(uri)
+ body = self.deserialize_single(body)
+ self.expected_success(200, resp.status)
+ return service_client.ResponseBody(resp, body)
+
+ def show_minimum_bandwidth_rule(self, policy_id, rule_id):
+ uri = '%s/qos/policies/%s/minimum_bandwidth_rules/%s' % (
+ self.uri_prefix, policy_id, rule_id)
+ resp, body = self.get(uri)
+ body = self.deserialize_single(body)
+ self.expected_success(200, resp.status)
+ return service_client.ResponseBody(resp, body)
+
+ def update_minimum_bandwidth_rule(self, policy_id, rule_id, **kwargs):
+ uri = '%s/qos/policies/%s/minimum_bandwidth_rules/%s' % (
+ self.uri_prefix, policy_id, rule_id)
+ post_data = {'minimum_bandwidth_rule': kwargs}
+ resp, body = self.put(uri, jsonutils.dumps(post_data))
+ body = self.deserialize_single(body)
+ self.expected_success(200, resp.status)
+ return service_client.ResponseBody(resp, body)
+
+ def delete_minimum_bandwidth_rule(self, policy_id, rule_id):
+ uri = '%s/qos/policies/%s/minimum_bandwidth_rules/%s' % (
+ self.uri_prefix, policy_id, rule_id)
+ resp, body = self.delete(uri)
+ self.expected_success(204, resp.status)
+ return service_client.ResponseBody(resp, body)
+
def list_qos_rule_types(self):
uri = '%s/qos/rule-types' % self.uri_prefix
resp, body = self.get(uri)
@@ -660,7 +708,8 @@
return service_client.ResponseBody(resp, body)
def create_trunk(self, parent_port_id, subports,
- tenant_id=None, name=None, admin_state_up=None):
+ tenant_id=None, name=None, admin_state_up=None,
+ description=None):
uri = '%s/trunks' % self.uri_prefix
post_data = {
'trunk': {
@@ -673,6 +722,8 @@
post_data['trunk']['tenant_id'] = tenant_id
if name is not None:
post_data['trunk']['name'] = name
+ if description is not None:
+ post_data['trunk']['description'] = description
if admin_state_up is not None:
post_data['trunk']['admin_state_up'] = admin_state_up
resp, body = self.post(uri, self.serialize(post_data))
@@ -738,6 +789,35 @@
body = jsonutils.loads(body)
return service_client.ResponseBody(resp, body)
+ def delete_auto_allocated_topology(self, tenant_id=None):
+ uri = '%s/auto-allocated-topology/%s' % (self.uri_prefix, tenant_id)
+ resp, body = self.delete(uri)
+ self.expected_success(204, resp.status)
+ return service_client.ResponseBody(resp, body)
+
+ def create_flavor_service_profile(self, flavor_id, service_profile_id):
+ body = jsonutils.dumps({'service_profile': {'id': service_profile_id}})
+ uri = '%s/flavors/%s/service_profiles' % (self.uri_prefix, flavor_id)
+ resp, body = self.post(uri, body)
+ self.expected_success(201, resp.status)
+ body = jsonutils.loads(body)
+ return service_client.ResponseBody(resp, body)
+
+ def list_flavor_service_profiles(self, flavor_id):
+ uri = '%s/flavors/%s/service_profiles' % (self.uri_prefix, flavor_id)
+ resp, body = self.get(uri)
+ self.expected_success(200, resp.status)
+ body = jsonutils.loads(body)
+ return service_client.ResponseBody(resp, body)
+
+ def delete_flavor_service_profile(self, flavor_id, service_profile_id):
+ uri = '%s/flavors/%s/service_profiles/%s' % (self.uri_prefix,
+ flavor_id,
+ service_profile_id)
+ resp, body = self.delete(uri)
+ self.expected_success(204, resp.status)
+ return service_client.ResponseBody(resp, body)
+
def create_security_group_rule(self, direction, security_group_id,
**kwargs):
post_body = {'security_group_rule': kwargs}
@@ -792,6 +872,21 @@
body = jsonutils.loads(body)
return service_client.ResponseBody(resp, body)
+ def create_network_keystone_v3(self, name, project_id, tenant_id=None):
+ uri = '%s/networks' % self.uri_prefix
+ post_data = {
+ 'network': {
+ 'name': name,
+ 'project_id': project_id
+ }
+ }
+ if tenant_id is not None:
+ post_data['network']['tenant_id'] = tenant_id
+ resp, body = self.post(uri, self.serialize(post_data))
+ body = self.deserialize_single(body)
+ self.expected_success(201, resp.status)
+ return service_client.ResponseBody(resp, body)
+
def list_extensions(self, **filters):
uri = self.get_uri("extensions")
if filters: