Merge "Fix typo in README.rst"
diff --git a/releasenotes/notes/add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml b/releasenotes/notes/add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
new file mode 100644
index 0000000..acc7a41
--- /dev/null
+++ b/releasenotes/notes/add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Add versions_client module for image service.
+ This new module provides list_versions() method which shows API versions
+ from Image service.
diff --git a/requirements.txt b/requirements.txt
index 6962e3e..92825a7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,7 +9,7 @@
netaddr!=0.7.16,>=0.7.13 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
oslo.concurrency>=3.8.0 # Apache-2.0
-oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
+oslo.config>=3.22.0 # Apache-2.0
oslo.log>=3.11.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
oslo.utils>=3.20.0 # Apache-2.0
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index 45472df..984f1a9 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -30,6 +30,9 @@
5. Swap volume from "volume1" to "volume2" as admin.
6. Check the swap volume is successful and "volume2"
is attached to "instance1" and "volume1" is in available state.
+ 7. Swap volume from "volume2" to "volume1" as admin.
+ 8. Check the swap volume is successful and "volume1"
+ is attached to "instance1" and "volume2" is in available state.
"""
@classmethod
@@ -58,13 +61,21 @@
volume1['id'], 'available')
waiters.wait_for_volume_resource_status(self.volumes_client,
volume2['id'], 'in-use')
- self.addCleanup(self.servers_client.detach_volume,
- server['id'], volume2['id'])
# Verify "volume2" is attached to the server
vol_attachments = self.servers_client.list_volume_attachments(
server['id'])['volumeAttachments']
self.assertEqual(1, len(vol_attachments))
self.assertIn(volume2['id'], vol_attachments[0]['volumeId'])
- # TODO(mriedem): Test swapping back from volume2 to volume1 after
- # nova bug 1490236 is fixed.
+ # Swap volume from "volume2" to "volume1"
+ self.admin_servers_client.update_attached_volume(
+ server['id'], volume2['id'], volumeId=volume1['id'])
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ volume2['id'], 'available')
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ volume1['id'], 'in-use')
+ # Verify "volume1" is attached to the server
+ vol_attachments = self.servers_client.list_volume_attachments(
+ server['id'])['volumeAttachments']
+ self.assertEqual(1, len(vol_attachments))
+ self.assertIn(volume1['id'], vol_attachments[0]['volumeId'])
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 69294fa..c586960 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -145,6 +145,7 @@
cls.namespace_objects_client = cls.os.namespace_objects_client
cls.namespace_tags_client = cls.os.namespace_tags_client
cls.schemas_client = cls.os.schemas_client
+ cls.versions_client = cls.os.image_versions_client
def create_namespace(cls, namespace_name=None, visibility='public',
description='Tempest', protected=False,
diff --git a/tempest/api/image/v2/test_versions.py b/tempest/api/image/v2/test_versions.py
new file mode 100644
index 0000000..24f104c
--- /dev/null
+++ b/tempest/api/image/v2/test_versions.py
@@ -0,0 +1,30 @@
+# Copyright 2017 NEC Corporation. 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 tempest.api.image import base
+from tempest.lib import decorators
+from tempest import test
+
+
+class VersionsTest(base.BaseV2ImageTest):
+
+ @decorators.idempotent_id('659ea30a-a17c-4317-832c-0f68ed23c31d')
+ @test.attr(type='smoke')
+ def test_list_versions(self):
+ versions = self.versions_client.list_versions()['versions']
+ expected_resources = ('id', 'links', 'status')
+
+ for version in versions:
+ for res in expected_resources:
+ self.assertIn(res, version)
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index fbfcafc..742fe59 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -239,6 +239,36 @@
'enable_snat': False})
self._verify_gateway_port(router['id'])
+ @decorators.idempotent_id('cbe42f84-04c2-11e7-8adb-fa163e4fa634')
+ @test.requires_ext(extension='ext-gw-mode', service='network')
+ @testtools.skipUnless(CONF.network.public_network_id,
+ 'The public_network_id option must be specified.')
+ def test_create_router_set_gateway_with_fixed_ip(self):
+ # Don't know public_network_address, so at first create address
+ # from public_network and delete
+ port = self.admin_ports_client.create_port(
+ network_id=CONF.network.public_network_id)['port']
+ self.admin_ports_client.delete_port(port_id=port['id'])
+
+ fixed_ip = {
+ 'subnet_id': port['fixed_ips'][0]['subnet_id'],
+ 'ip_address': port['fixed_ips'][0]['ip_address']
+ }
+ external_gateway_info = {
+ 'network_id': CONF.network.public_network_id,
+ 'external_fixed_ips': [fixed_ip]
+ }
+
+ # Create a router and set gateway to fixed_ip
+ router = self.admin_routers_client.create_router(
+ external_gateway_info=external_gateway_info)['router']
+ self.addCleanup(self.admin_routers_client.delete_router,
+ router_id=router['id'])
+ # Examine router's gateway is equal to fixed_ip
+ self.assertEqual(router['external_gateway_info'][
+ 'external_fixed_ips'][0]['ip_address'],
+ fixed_ip['ip_address'])
+
@decorators.idempotent_id('ad81b7ee-4f81-407b-a19c-17e623f763e8')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index f46b873..218a79a 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -176,6 +176,16 @@
name=name)
@test.attr(type=['negative'])
+ @decorators.idempotent_id('966e2b96-023a-11e7-a9e4-fa163e4fa634')
+ def test_create_security_group_update_name_default(self):
+ # Update security group name to 'default', it should be failed.
+ group_create_body, _ = self._create_security_group()
+ self.assertRaises(lib_exc.Conflict,
+ self.security_groups_client.update_security_group,
+ group_create_body['security_group']['id'],
+ name="default")
+
+ @test.attr(type=['negative'])
@decorators.idempotent_id('8fde898f-ce88-493b-adc9-4e4692879fc5')
def test_create_duplicate_security_group_rule_fails(self):
# Create duplicate security group rule, it should fail.
diff --git a/tempest/clients.py b/tempest/clients.py
index 9cb918a..e75fa79 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -125,6 +125,8 @@
self.image_v2.NamespacePropertiesClient()
self.namespace_tags_client = \
self.image_v2.NamespaceTagsClient()
+ self.image_versions_client = \
+ self.image_v2.VersionsClient()
def _set_compute_clients(self):
self.agents_client = self.compute.AgentsClient()
diff --git a/tempest/lib/services/image/v2/__init__.py b/tempest/lib/services/image/v2/__init__.py
index 7d973e5..99a5321 100644
--- a/tempest/lib/services/image/v2/__init__.py
+++ b/tempest/lib/services/image/v2/__init__.py
@@ -25,7 +25,9 @@
from tempest.lib.services.image.v2.resource_types_client import \
ResourceTypesClient
from tempest.lib.services.image.v2.schemas_client import SchemasClient
+from tempest.lib.services.image.v2.versions_client import VersionsClient
__all__ = ['ImageMembersClient', 'ImagesClient', 'NamespaceObjectsClient',
'NamespacePropertiesClient', 'NamespaceTagsClient',
- 'NamespacesClient', 'ResourceTypesClient', 'SchemasClient']
+ 'NamespacesClient', 'ResourceTypesClient', 'SchemasClient',
+ 'VersionsClient']
diff --git a/tempest/lib/services/image/v2/versions_client.py b/tempest/lib/services/image/v2/versions_client.py
new file mode 100644
index 0000000..1adc466
--- /dev/null
+++ b/tempest/lib/services/image/v2/versions_client.py
@@ -0,0 +1,38 @@
+# Copyright 2017 NEC Corporation. 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.
+
+import time
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class VersionsClient(rest_client.RestClient):
+ api_version = "v2"
+
+ def list_versions(self):
+ """List API versions"""
+ version_url = self._get_base_version_url()
+
+ start = time.time()
+ resp, body = self.raw_request(version_url, 'GET')
+ end = time.time()
+ self._log_request('GET', version_url, resp, secs=(end - start),
+ resp_body=body)
+ self._error_checker(resp, body)
+
+ self.expected_success(300, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/tests/lib/services/image/v2/test_versions_client.py b/tempest/tests/lib/services/image/v2/test_versions_client.py
new file mode 100644
index 0000000..6234b06
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_versions_client.py
@@ -0,0 +1,94 @@
+# Copyright 2017 NEC Corporation. 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 tempest.lib.services.image.v2 import versions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVersionsClient(base.BaseServiceTest):
+
+ FAKE_VERSIONS_INFO = {
+ "versions": [
+ {
+ "status": "CURRENT", "id": "v2.5",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "SUPPORTED", "id": "v2.4",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "SUPPORTED", "id": "v2.3",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "SUPPORTED", "id": "v2.2",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "SUPPORTED", "id": "v2.1",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "SUPPORTED", "id": "v2.0",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "DEPRECATED", "id": "v1.1",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v1/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "DEPRECATED", "id": "v1.0",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v1/", "rel": "self"}
+ ]
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestVersionsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = versions_client.VersionsClient(fake_auth,
+ 'image',
+ 'regionOne')
+
+ def _test_list_versions(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_versions,
+ 'tempest.lib.common.rest_client.RestClient.raw_request',
+ self.FAKE_VERSIONS_INFO,
+ bytes_body,
+ 300)
+
+ def test_list_versions_with_str_body(self):
+ self._test_list_versions()
+
+ def test_list_versions_with_bytes_body(self):
+ self._test_list_versions(bytes_body=True)