Merge "Cap nova-network tests at Queens"
diff --git a/HACKING.rst b/HACKING.rst
index bb55ac5..1c084f8 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -419,3 +419,34 @@
tested is considered stable and adheres to the OpenStack API stability
guidelines. If an API is still considered experimental or in development then
it should not be tested by Tempest until it is considered stable.
+
+Stable Support Policy
+---------------------
+
+Since the `Extended Maintenance policy`_ for stable branches was adopted
+OpenStack projects will keep stable branches around after a "stable" or
+"maintained" period for a phase of indeterminate length called "Extended
+Maintenance". Prior to this resolution Tempest supported all stable branches
+which were supported upstream. This policy does not scale under the new model
+as Tempest would be responsible for gating proposed changes against an ever
+increasing number of branches. Therefore due to resource constraints, Tempest
+will only provide support for branches in the "Maintained" phase from the
+documented `Support Phases`_. When a branch moves from the *Maintained* to the
+*Extended Maintenance* phase, Tempest will tag the removal of support for that
+branch as it has in the past when a branch goes end of life.
+
+The expectation for *Extended Maintenance* phase branches is that they will continue
+running Tempest during that phase of support. Since the REST APIs are stable
+interfaces across release boundaries, branches in these phases should run
+Tempest from master as long as possible. But, because we won't be actively
+testing branches in these phases, it's possible that we'll introduce changes to
+Tempest on master which will break support on *Extended Maintenance* phase
+branches. When this happens the expectation for those branches is to either
+switch to running Tempest from a tag with support for the branch, or blacklist
+a newly introduced test (if that is the cause of the issue). Tempest will not
+be creating stable branches to support *Extended Maintenance* phase branches, as
+the burden is on the *Extended Maintenance* phase branche maintainers, not the Tempest
+project, to support that branch.
+
+.. _Extended Maintenance policy: https://governance.openstack.org/tc/resolutions/20180301-stable-branch-eol.html
+.. _Support Phases: https://docs.openstack.org/project-team-guide/stable-branches.html#maintenance-phases
diff --git a/README.rst b/README.rst
index 044ae09..2243536 100644
--- a/README.rst
+++ b/README.rst
@@ -212,18 +212,6 @@
For more information on these options and details about stestr, please see the
`stestr documentation <http://stestr.readthedocs.io/en/latest/MANUAL.html>`_.
-Python 2.6
-----------
-
-Starting in the Kilo release the OpenStack services dropped all support for
-python 2.6. This change has been mirrored in Tempest, starting after the
-tempest-2 tag. This means that proposed changes to Tempest which only fix
-python 2.6 compatibility will be rejected, and moving forward more features not
-present in python 2.6 will be used. If you're running your OpenStack services
-on an earlier release with python 2.6 you can easily run Tempest against it
-from a remote system running python 2.7. (or deploy a cloud guest in your cloud
-that has python 2.7)
-
Python 3.x
----------
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index fa76770..f2c51b3 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -366,6 +366,10 @@
.. _2.55: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id49
+ * `2.57`_
+
+ .. _2.57: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id51
+
* `2.60`_
.. _2.60: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id54
diff --git a/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml b/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
index fc061bc..9ae46fd 100644
--- a/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
+++ b/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
@@ -6,4 +6,4 @@
The lack of these additional content-type will cause defcore test
to fail for OpenStack public cloud which uses tomcat module in the
api gateway. The additions are ``application/json;charset=utf-8``,
- ``text/html;charset=utf-8``,``text/plain;charset=utf-8``
\ No newline at end of file
+ ``text/html;charset=utf-8``, ``text/plain;charset=utf-8``
diff --git a/releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml b/releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
index 73900ca..e9c3694 100644
--- a/releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
+++ b/releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
@@ -1,6 +1,6 @@
---
features:
- |
- Adds a new cli option to tempest run, --combine, which is used to indicate
- you want the subunit stream output combined with the previous run's in
- the testr repository
+ Adds a new cli option to tempest run, ``--combine``, which is used
+ to indicate you want the subunit stream output combined with the
+ previous run's in the testr repository
diff --git a/releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml b/releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
index 9d7102f..5b4a96d 100644
--- a/releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
+++ b/releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
@@ -1,5 +1,5 @@
---
upgrade:
- |
- The deprecated config option 'allow_port_security_disabled' from compute_feature_enabled
- group has been removed.
+ The deprecated config option ``allow_port_security_disabled`` from
+ ``compute_feature_enabled`` group has been removed.
diff --git a/releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml b/releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
index b4e4dd1..c8b0ca8 100644
--- a/releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
+++ b/releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
@@ -8,4 +8,4 @@
- ``compute.ssh_user`` (available as ``validation.image_ssh_user``)
- ``scenario.ssh_user`` (available as ``validation.image_ssh_user``)
- ``compute.network_for_ssh`` (available as ``validation.network_for_ssh``)
- - ``compute.ping_timeout `` (available as ``validation.ping_timeout``)
+ - ``compute.ping_timeout`` (available as ``validation.ping_timeout``)
diff --git a/releasenotes/notes/add-load-list-cmd-35a4a2e6ea0a36fd.yaml b/releasenotes/notes/add-load-list-cmd-35a4a2e6ea0a36fd.yaml
index 403bbad..145e7dd 100644
--- a/releasenotes/notes/add-load-list-cmd-35a4a2e6ea0a36fd.yaml
+++ b/releasenotes/notes/add-load-list-cmd-35a4a2e6ea0a36fd.yaml
@@ -1,7 +1,7 @@
---
features:
- |
- Adds a new cli option to tempest run, --load-list <list-file>
+ Adds a new cli option to tempest run, ``--load-list <list-file>``
to specify target tests to run from a list-file. The list-file
- supports the output format of the tempest run --list-tests
+ supports the output format of the tempest run ``--list-tests``
command.
diff --git a/releasenotes/notes/add-save-state-option-5ea67858cbaca969.yaml b/releasenotes/notes/add-save-state-option-5ea67858cbaca969.yaml
index 8fdf4f0..abd2610 100644
--- a/releasenotes/notes/add-save-state-option-5ea67858cbaca969.yaml
+++ b/releasenotes/notes/add-save-state-option-5ea67858cbaca969.yaml
@@ -1,4 +1,5 @@
---
features:
- |
- Add --save-state option to allow saving state of cloud before tempest run.
+ Add ``--save-state`` option to allow saving state of cloud before
+ tempest run.
diff --git a/releasenotes/notes/add-show-quota-details-api-to-network-quotas-client-3fffd302cc5d335f.yaml b/releasenotes/notes/add-show-quota-details-api-to-network-quotas-client-3fffd302cc5d335f.yaml
index 406e282..6c44ba0 100644
--- a/releasenotes/notes/add-show-quota-details-api-to-network-quotas-client-3fffd302cc5d335f.yaml
+++ b/releasenotes/notes/add-show-quota-details-api-to-network-quotas-client-3fffd302cc5d335f.yaml
@@ -3,5 +3,5 @@
- |
Add extension API show quota details to network quotas_client library.
This feature enables the possibility to show a quota set for a specified
- project that includes the quota’s used, limit and reserved counts for per
- resource
+ project that includes the quota's used, limit and reserved counts per
+ resource.
diff --git a/releasenotes/notes/cli-tests-v3fixes-fb38189cefd64213.yaml b/releasenotes/notes/cli-tests-v3fixes-fb38189cefd64213.yaml
index e3443c8..2a0a86c 100644
--- a/releasenotes/notes/cli-tests-v3fixes-fb38189cefd64213.yaml
+++ b/releasenotes/notes/cli-tests-v3fixes-fb38189cefd64213.yaml
@@ -2,8 +2,8 @@
other:
- |
The CLIClient class, when it calls a command line client, uses
- --os-project-name instead of --os-tenant-name for the project, and
- passes --os-identity-api-version (default empty).
+ ``--os-project-name`` instead of ``--os-tenant-name`` for the
+ project, and passes ``--os-identity-api-version`` (default empty).
All CLI clients still available in supported releases of OpenStack
- which are wrapped by the cmd_with_auth() method support those
+ which are wrapped by the ``cmd_with_auth()`` method support those
switches.
diff --git a/releasenotes/notes/tempest-run-fix-updates-564b41706decbba1.yaml b/releasenotes/notes/tempest-run-fix-updates-564b41706decbba1.yaml
index 265853d..0f9a0f6 100644
--- a/releasenotes/notes/tempest-run-fix-updates-564b41706decbba1.yaml
+++ b/releasenotes/notes/tempest-run-fix-updates-564b41706decbba1.yaml
@@ -1,8 +1,8 @@
---
features:
- |
- Adds a new CLI arg in tempest run, --black-regex, which is a regex to
- exclude the tests that match it.
+ Adds a new CLI arg in tempest run, ``--black-regex``, which is a
+ regex to exclude the tests that match it.
fixes:
- |
Fixes tempest run CLI args mutually exclusive behavior which should not
diff --git a/releasenotes/notes/tempest-workspace-delete-directory-feature-74d6d157a5a05561.yaml b/releasenotes/notes/tempest-workspace-delete-directory-feature-74d6d157a5a05561.yaml
index ec21098..c69ed50 100644
--- a/releasenotes/notes/tempest-workspace-delete-directory-feature-74d6d157a5a05561.yaml
+++ b/releasenotes/notes/tempest-workspace-delete-directory-feature-74d6d157a5a05561.yaml
@@ -1,5 +1,5 @@
---
features:
- |
- Added tempest workspace remove --name <workspace_name> --rmdir
+ Added tempest workspace remove ``--name <workspace_name> --rmdir``
feature to delete the workspace directory as well as entry.
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 3a85a86..ff8ed61 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -13,7 +13,6 @@
# under the License.
import json
-import time
from oslo_log import log as logging
@@ -80,9 +79,11 @@
return True
cmd = 'curl %s' % md_url
md_json = ssh_client.exec_command(cmd)
- verify_method(md_json)
- return True
-
+ return verify_method(md_json)
+ # NOTE(gmann) Keep refreshing the metadata info until the metadata
+ # cache is refreshed. For safer side, we will go with wait loop of
+ # build_interval till build_timeout. verify_method() above will return
+ # True if all metadata verification is done as expected.
if not test_utils.call_until_true(get_and_verify_metadata,
CONF.compute.build_timeout,
CONF.compute.build_interval):
@@ -122,16 +123,20 @@
if d['mac'] == self.net_2_200_mac:
self.assertEqual(d['tags'], ['net-2-200'])
- # A hypervisor may present multiple paths to a tagged disk, so
- # there may be duplicated tags in the metadata, use set() to
- # remove duplicated tags.
- # Some hypervisors might report devices with no tags as well.
- found_devices = [d['tags'][0] for d in md_dict['devices']
- if d.get('tags')]
+ # A hypervisor may present multiple paths to a tagged disk, so
+ # there may be duplicated tags in the metadata, use set() to
+ # remove duplicated tags.
+ # Some hypervisors might report devices with no tags as well.
+ found_devices = [d['tags'][0] for d in md_dict['devices']
+ if d.get('tags')]
+ try:
self.assertEqual(set(found_devices), set(['port-1', 'port-2',
'net-1', 'net-2-100',
'net-2-200', 'boot',
'other']))
+ return True
+ except Exception:
+ return False
@decorators.idempotent_id('a2e65a6c-66f1-4442-aaa8-498c31778d96')
@utils.services('network', 'volume', 'image')
@@ -302,12 +307,21 @@
def verify_device_metadata(self, md_json):
md_dict = json.loads(md_json)
- found_devices = [d['tags'][0] for d in md_dict['devices']]
- self.assertItemsEqual(found_devices, ['nic-tag', 'volume-tag'])
+ found_devices = [d['tags'][0] for d in md_dict['devices']
+ if d.get('tags')]
+ try:
+ self.assertItemsEqual(found_devices, ['nic-tag', 'volume-tag'])
+ return True
+ except Exception:
+ return False
def verify_empty_devices(self, md_json):
md_dict = json.loads(md_json)
- self.assertEmpty(md_dict['devices'])
+ try:
+ self.assertEmpty(md_dict['devices'])
+ return True
+ except Exception:
+ return False
@decorators.idempotent_id('3e41c782-2a89-4922-a9d2-9a188c4e7c7c')
@utils.services('network', 'volume', 'image')
@@ -354,10 +368,6 @@
server=server,
servers_client=self.servers_client)
- # NOTE(artom) The newly attached tagged nic won't appear in the
- # metadata until the cache is refreshed. We wait 16 seconds since the
- # default cache expiry is 15 seconds.
- time.sleep(16)
self.verify_metadata_from_api(server, ssh_client,
self.verify_device_metadata)
@@ -370,7 +380,5 @@
waiters.wait_for_interface_detach(self.interfaces_client,
server['id'],
interface['port_id'])
- # NOTE(artom) More waiting until metadata cache is refreshed.
- time.sleep(16)
self.verify_metadata_from_api(server, ssh_client,
self.verify_empty_devices)
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index bbec30c..ec7b505 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -603,6 +603,20 @@
waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
glance_client.wait_for_resource_deletion(images[0]['id'])
+ @decorators.idempotent_id('8cf9f450-a871-42cf-9bef-77eba189c0b0')
+ @decorators.related_bug('1745529')
+ @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
+ 'Shelve is not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+ 'Pause is not available.')
+ def test_shelve_paused_server(self):
+ server = self.create_test_server(wait_until='ACTIVE')
+ self.client.pause_server(server['id'])
+ waiters.wait_for_server_status(self.client, server['id'], 'PAUSED')
+ # Check if Shelve operation is successful on paused server.
+ compute.shelve_server(self.client, server['id'],
+ force_shelve_offload=True)
+
@decorators.idempotent_id('af8eafd4-38a7-4a4b-bdbc-75145a580560')
def test_stop_start_server(self):
self.client.stop_server(self.server_id)
diff --git a/tempest/api/compute/servers/test_servers_microversions.py b/tempest/api/compute/servers/test_servers_microversions.py
index f3863f1..2434884 100644
--- a/tempest/api/compute/servers/test_servers_microversions.py
+++ b/tempest/api/compute/servers/test_servers_microversions.py
@@ -49,3 +49,18 @@
key_name=keypair_name)
waiters.wait_for_server_status(self.servers_client,
server['id'], 'ACTIVE')
+
+
+class ServerShowV257Test(base.BaseV2ComputeTest):
+ min_microversion = '2.57'
+ max_microversion = 'latest'
+
+ @decorators.idempotent_id('803df848-080a-4261-8f11-b020cd9b6f60')
+ def test_rebuild_server(self):
+ server = self.create_test_server(wait_until='ACTIVE')
+ user_data = "ZWNobyAiaGVsbG8gd29ybGQi"
+ # Checking rebuild API response schema
+ self.servers_client.rebuild_server(server['id'], self.image_ref_alt,
+ user_data=user_data)
+ waiters.wait_for_server_status(self.servers_client,
+ server['id'], 'ACTIVE')
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 375aacb..35eea11 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -59,9 +59,7 @@
volume = self.create_volume()
# Create backup
backup_name = data_utils.rand_name(self.__class__.__name__ + '-Backup')
- backup = (self.create_backup(backup_client=self.admin_backups_client,
- volume_id=volume['id'],
- name=backup_name))
+ backup = self.create_backup(volume_id=volume['id'], name=backup_name)
self.assertEqual(backup_name, backup['name'])
# Export Backup
@@ -103,17 +101,16 @@
self.assertIn(new_id, [b['id'] for b in backups])
# Restore backup
- restore = self.admin_backups_client.restore_backup(
- backup['id'])['restore']
- self.addCleanup(self.admin_volume_client.delete_volume,
+ restore = self.backups_client.restore_backup(backup['id'])['restore']
+ self.addCleanup(self.volumes_client.delete_volume,
restore['volume_id'])
self.assertEqual(backup['id'], restore['backup_id'])
- waiters.wait_for_volume_resource_status(self.admin_volume_client,
+ waiters.wait_for_volume_resource_status(self.volumes_client,
restore['volume_id'],
'available')
# Verify if restored volume is there in volume list
- volumes = self.admin_volume_client.list_volumes()['volumes']
+ volumes = self.volumes_client.list_volumes()['volumes']
self.assertIn(restore['volume_id'], [v['id'] for v in volumes])
waiters.wait_for_volume_resource_status(self.admin_backups_client,
import_backup['id'],
@@ -126,12 +123,10 @@
# Create a backup
backup_name = data_utils.rand_name(
self.__class__.__name__ + '-Backup')
- backup = self.create_backup(backup_client=self.admin_backups_client,
- volume_id=volume['id'],
- name=backup_name)
+ backup = self.create_backup(volume_id=volume['id'], name=backup_name)
self.assertEqual(backup_name, backup['name'])
# Reset backup status to error
self.admin_backups_client.reset_backup_status(backup_id=backup['id'],
status="error")
- waiters.wait_for_volume_resource_status(self.admin_backups_client,
+ waiters.wait_for_volume_resource_status(self.backups_client,
backup['id'], 'error')
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 4108da5..75e81b7 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -15,6 +15,7 @@
from tempest.api.volume import base
from tempest.common import waiters
+from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
@@ -43,6 +44,9 @@
transfer = self.client.create_volume_transfer(
volume_id=volume['id'])['transfer']
transfer_id = transfer['id']
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.client.delete_volume_transfer,
+ transfer_id)
auth_key = transfer['auth_key']
waiters.wait_for_volume_resource_status(
self.volumes_client, volume['id'], 'awaiting-transfer')
@@ -81,6 +85,9 @@
# Create a volume transfer
transfer_id = self.client.create_volume_transfer(
volume_id=volume['id'])['transfer']['id']
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.client.delete_volume_transfer,
+ transfer_id)
waiters.wait_for_volume_resource_status(
self.volumes_client, volume['id'], 'awaiting-transfer')
diff --git a/tempest/clients.py b/tempest/clients.py
index 707127c..2a07be9 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -241,32 +241,32 @@
# if only api_v3 is enabled, all these clients should be available
if (CONF.volume_feature_enabled.api_v2 or
CONF.volume_feature_enabled.api_v3):
- self.backups_v2_client = self.volume_v2.BackupsClient()
+ self.backups_v2_client = self.volume_v3.BackupsClient()
self.encryption_types_v2_client = \
- self.volume_v2.EncryptionTypesClient()
+ self.volume_v3.EncryptionTypesClient()
self.snapshot_manage_v2_client = \
- self.volume_v2.SnapshotManageClient()
- self.snapshots_v2_client = self.volume_v2.SnapshotsClient()
+ self.volume_v3.SnapshotManageClient()
+ self.snapshots_v2_client = self.volume_v3.SnapshotsClient()
self.volume_capabilities_v2_client = \
- self.volume_v2.CapabilitiesClient()
- self.volume_manage_v2_client = self.volume_v2.VolumeManageClient()
- self.volume_qos_v2_client = self.volume_v2.QosSpecsClient()
- self.volume_services_v2_client = self.volume_v2.ServicesClient()
- self.volume_types_v2_client = self.volume_v2.TypesClient()
- self.volume_hosts_v2_client = self.volume_v2.HostsClient()
- self.volume_quotas_v2_client = self.volume_v2.QuotasClient()
+ self.volume_v3.CapabilitiesClient()
+ self.volume_manage_v2_client = self.volume_v3.VolumeManageClient()
+ self.volume_qos_v2_client = self.volume_v3.QosSpecsClient()
+ self.volume_services_v2_client = self.volume_v3.ServicesClient()
+ self.volume_types_v2_client = self.volume_v3.TypesClient()
+ self.volume_hosts_v2_client = self.volume_v3.HostsClient()
+ self.volume_quotas_v2_client = self.volume_v3.QuotasClient()
self.volume_quota_classes_v2_client = \
- self.volume_v2.QuotaClassesClient()
+ self.volume_v3.QuotaClassesClient()
self.volume_scheduler_stats_v2_client = \
- self.volume_v2.SchedulerStatsClient()
+ self.volume_v3.SchedulerStatsClient()
self.volume_transfers_v2_client = \
- self.volume_v2.TransfersClient()
+ self.volume_v3.TransfersClient()
self.volume_v2_availability_zone_client = \
- self.volume_v2.AvailabilityZoneClient()
- self.volume_v2_limits_client = self.volume_v2.LimitsClient()
- self.volumes_v2_client = self.volume_v2.VolumesClient()
+ self.volume_v3.AvailabilityZoneClient()
+ self.volume_v2_limits_client = self.volume_v3.LimitsClient()
+ self.volumes_v2_client = self.volume_v3.VolumesClient()
self.volumes_v2_extension_client = \
- self.volume_v2.ExtensionsClient()
+ self.volume_v3.ExtensionsClient()
# Set default client for users that don't need explicit version
self.volumes_client_latest = self.volumes_v2_client
diff --git a/tempest/lib/api_schema/response/compute/v2_57/__init__.py b/tempest/lib/api_schema/response/compute/v2_57/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_57/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_57/servers.py b/tempest/lib/api_schema/response/compute/v2_57/servers.py
new file mode 100644
index 0000000..ed1ca7d
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_57/servers.py
@@ -0,0 +1,53 @@
+# 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_54 import servers as servers254
+# ****** Schemas changed in microversion 2.57 *****************
+
+# Note(gmann): This is schema for microversion 2.57 which includes the
+# 'user_data' in the Response body of the following APIs:
+# - ``POST '/servers/{server_id}/action (rebuild)``
+
+user_data = {
+ 'oneOf': [
+ {
+ 'type': 'string',
+ 'format': 'base64',
+ 'maxLength': 65535
+ },
+ {'type': 'null'}
+ ]
+}
+
+rebuild_server = copy.deepcopy(servers254.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+ 'properties'].update({'user_data': user_data})
+rebuild_server['response_body']['properties']['server'][
+ 'required'].append('user_data')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+ servers254.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+ 'properties'].update({'user_data': user_data})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+ 'required'].append('user_data')
+
+# ****** Schemas unchanged in microversion 2.57 since microversion 2.54 ***
+
+# NOTE(gmann): Below are the unchanged schema in this microversion. We need
+# to keeo this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+get_server = copy.deepcopy(servers254.get_server)
+list_servers_detail = copy.deepcopy(servers254.list_servers_detail)
+update_server = copy.deepcopy(servers254.update_server)
diff --git a/tempest/lib/common/jsonschema_validator.py b/tempest/lib/common/jsonschema_validator.py
index bbdf382..9a35b76 100644
--- a/tempest/lib/common/jsonschema_validator.py
+++ b/tempest/lib/common/jsonschema_validator.py
@@ -12,9 +12,11 @@
# 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 base64
import jsonschema
from oslo_utils import timeutils
+import six
# JSON Schema validator and format checker used for JSON Schema validation
JSONSCHEMA_VALIDATOR = jsonschema.Draft4Validator
@@ -37,3 +39,19 @@
return False
else:
return True
+
+
+@jsonschema.FormatChecker.cls_checks('base64')
+def _validate_base64_format(instance):
+ try:
+ if isinstance(instance, six.text_type):
+ instance = instance.encode('utf-8')
+ base64.decodestring(instance)
+ except base64.binascii.Error:
+ return False
+ except TypeError:
+ # The name must be string type. If instance isn't string type, the
+ # TypeError will be raised at here.
+ return False
+
+ return True
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index e75cdb5..f6e0e69 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -30,6 +30,7 @@
from tempest.lib.api_schema.response.compute.v2_47 import servers as schemav247
from tempest.lib.api_schema.response.compute.v2_48 import servers as schemav248
from tempest.lib.api_schema.response.compute.v2_54 import servers as schemav254
+from tempest.lib.api_schema.response.compute.v2_57 import servers as schemav257
from tempest.lib.api_schema.response.compute.v2_6 import servers as schemav26
from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
from tempest.lib.common import rest_client
@@ -49,7 +50,8 @@
{'min': '2.26', 'max': '2.46', 'schema': schemav226},
{'min': '2.47', 'max': '2.47', 'schema': schemav247},
{'min': '2.48', 'max': '2.53', 'schema': schemav248},
- {'min': '2.54', 'max': None, 'schema': schemav254}]
+ {'min': '2.54', 'max': '2.56', 'schema': schemav254},
+ {'min': '2.57', 'max': None, 'schema': schemav257}]
def __init__(self, auth_provider, service, region,
enable_instance_password=True, **kwargs):
diff --git a/tempest/lib/services/network/agents_client.py b/tempest/lib/services/network/agents_client.py
index 9bdf090..a0f832e 100644
--- a/tempest/lib/services/network/agents_client.py
+++ b/tempest/lib/services/network/agents_client.py
@@ -18,35 +18,62 @@
class AgentsClient(base.BaseNetworkClient):
def update_agent(self, agent_id, **kwargs):
- """Update agent."""
- # TODO(piyush): Current api-site doesn't contain this API description.
- # After fixing the api-site, we need to fix here also for putting the
- # link to api-site.
- # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526673
+ """Update an agent.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/network/v2/#update-agent
+ """
uri = '/agents/%s' % agent_id
return self.update_resource(uri, kwargs)
def show_agent(self, agent_id, **fields):
+ """Show details for an agent.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/network/v2/#show-agent-details
+ """
uri = '/agents/%s' % agent_id
return self.show_resource(uri, **fields)
def list_agents(self, **filters):
+ """List all agents.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/network/v2/#list-all-agents
+ """
uri = '/agents'
return self.list_resources(uri, **filters)
def list_routers_on_l3_agent(self, agent_id):
+ """List routers that an l3 agent hosts.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/network/v2/#list-routers-hosted-by-an-l3-agent
+ """
uri = '/agents/%s/l3-routers' % agent_id
return self.list_resources(uri)
def create_router_on_l3_agent(self, agent_id, **kwargs):
- # TODO(piyush): Current api-site doesn't contain this API description.
- # After fixing the api-site, we need to fix here also for putting the
- # link to api-site.
- # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526670
+ """Add a router to an l3 agent.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/network/v2/#schedule-router-to-an-l3-agent
+ """
uri = '/agents/%s/l3-routers' % agent_id
return self.create_resource(uri, kwargs, expect_empty_body=True)
def delete_router_from_l3_agent(self, agent_id, router_id):
+ """Remove a router to an l3 agent.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/network/v2/#remove-l3-router-from-an-l3-agent
+ """
uri = '/agents/%s/l3-routers/%s' % (agent_id, router_id)
return self.delete_resource(uri)
diff --git a/tempest/tests/lib/common/test_http.py b/tempest/tests/lib/common/test_http.py
index a292209..02436e0 100644
--- a/tempest/tests/lib/common/test_http.py
+++ b/tempest/tests/lib/common/test_http.py
@@ -12,57 +12,158 @@
# License for the specific language governing permissions and limitations
# under the License.
+import urllib3
+
from tempest.lib.common import http
from tempest.tests import base
+CERT_NONE = 'CERT_NONE'
+CERT_REQUIRED = 'CERT_REQUIRED'
+CERT_LOCATION = '/etc/ssl/certs/ca-certificates.crt'
+PROXY_URL = 'http://myproxy:3128'
+REQUEST_URL = 'http://10.0.0.107:5000/v2.0'
+REQUEST_METHOD = 'GET'
+
+
class TestClosingHttp(base.TestCase):
- def setUp(self):
- super(TestClosingHttp, self).setUp()
- self.cert_none = "CERT_NONE"
- self.cert_location = "/etc/ssl/certs/ca-certificates.crt"
- def test_constructor_invalid_ca_certs_and_timeout(self):
- connection = http.ClosingHttp(
- disable_ssl_certificate_validation=False,
- ca_certs=None,
- timeout=None)
- for attr in ('cert_reqs', 'ca_certs', 'timeout'):
- self.assertNotIn(attr, connection.connection_pool_kw)
+ def closing_http(self, **kwargs):
+ return http.ClosingHttp(**kwargs)
- def test_constructor_valid_ca_certs(self):
- cert_required = 'CERT_REQUIRED'
- connection = http.ClosingHttp(
- disable_ssl_certificate_validation=False,
- ca_certs=self.cert_location,
- timeout=None)
- self.assertEqual(cert_required,
+ def test_closing_http(self):
+ connection = self.closing_http()
+
+ self.assertNotIn('cert_reqs', connection.connection_pool_kw)
+ self.assertNotIn('ca_certs', connection.connection_pool_kw)
+ self.assertNotIn('timeout', connection.connection_pool_kw)
+
+ def test_closing_http_with_ca_certs(self):
+ connection = self.closing_http(ca_certs=CERT_LOCATION)
+
+ self.assertEqual(CERT_REQUIRED,
connection.connection_pool_kw['cert_reqs'])
- self.assertEqual(self.cert_location,
+ self.assertEqual(CERT_LOCATION,
connection.connection_pool_kw['ca_certs'])
- self.assertNotIn('timeout',
+
+ def test_closing_http_with_dscv(self):
+ connection = self.closing_http(
+ disable_ssl_certificate_validation=True)
+
+ self.assertEqual(CERT_NONE,
+ connection.connection_pool_kw['cert_reqs'])
+ self.assertNotIn('ca_certs',
connection.connection_pool_kw)
- def test_constructor_ssl_cert_validation_disabled(self):
- connection = http.ClosingHttp(
+ def test_closing_http_with_ca_certs_and_dscv(self):
+ connection = self.closing_http(
disable_ssl_certificate_validation=True,
- ca_certs=None,
- timeout=30)
- self.assertEqual(self.cert_none,
+ ca_certs=CERT_LOCATION)
+
+ self.assertEqual(CERT_NONE,
connection.connection_pool_kw['cert_reqs'])
- self.assertEqual(30,
+ self.assertNotIn('ca_certs',
+ connection.connection_pool_kw)
+
+ def test_closing_http_with_timeout(self):
+ timeout = 30
+ connection = self.closing_http(timeout=timeout)
+ self.assertEqual(timeout,
connection.connection_pool_kw['timeout'])
- self.assertNotIn('ca_certs',
- connection.connection_pool_kw)
- def test_constructor_ssl_cert_validation_disabled_and_ca_certs(self):
- connection = http.ClosingHttp(
- disable_ssl_certificate_validation=True,
- ca_certs=self.cert_location,
- timeout=None)
- self.assertNotIn('timeout',
- connection.connection_pool_kw)
- self.assertEqual(self.cert_none,
- connection.connection_pool_kw['cert_reqs'])
- self.assertNotIn('ca_certs',
- connection.connection_pool_kw)
+ def test_request(self):
+ # Given
+ connection = self.closing_http()
+ http_response = urllib3.HTTPResponse()
+ request = self.patch('urllib3.PoolManager.request',
+ return_value=http_response)
+ retry = self.patch('urllib3.util.Retry')
+
+ # When
+ response, data = connection.request(
+ method=REQUEST_METHOD,
+ url=REQUEST_URL)
+
+ # Then
+ request.assert_called_once_with(
+ REQUEST_METHOD,
+ REQUEST_URL,
+ headers={'connection': 'close'},
+ retries=retry(raise_on_redirect=False, redirect=5))
+ self.assertEqual(
+ {'content-location': REQUEST_URL,
+ 'status': str(http_response.status)},
+ response)
+ self.assertEqual(http_response.status, response.status)
+ self.assertEqual(http_response.reason, response.reason)
+ self.assertEqual(http_response.version, response.version)
+ self.assertEqual(http_response.data, data)
+
+ def test_request_with_fields(self):
+ # Given
+ connection = self.closing_http()
+ http_response = urllib3.HTTPResponse()
+ request = self.patch('urllib3.PoolManager.request',
+ return_value=http_response)
+ retry = self.patch('urllib3.util.Retry')
+ fields = object()
+
+ # When
+ connection.request(
+ method=REQUEST_METHOD,
+ url=REQUEST_URL,
+ fields=fields)
+
+ # Then
+ request.assert_called_once_with(
+ REQUEST_METHOD,
+ REQUEST_URL,
+ fields=fields,
+ headers=dict(connection='close'),
+ retries=retry(raise_on_redirect=False, redirect=5))
+
+ def test_request_with_headers(self):
+ # Given
+ connection = self.closing_http()
+ headers = {'Xtra Key': 'Xtra Value'}
+ http_response = urllib3.HTTPResponse(headers=headers)
+ request = self.patch('urllib3.PoolManager.request',
+ return_value=http_response)
+ retry = self.patch('urllib3.util.Retry')
+
+ # When
+ response, _ = connection.request(
+ method=REQUEST_METHOD,
+ url=REQUEST_URL,
+ headers=headers)
+
+ # Then
+ request.assert_called_once_with(
+ REQUEST_METHOD,
+ REQUEST_URL,
+ headers=dict(headers, connection='close'),
+ retries=retry(raise_on_redirect=False, redirect=5))
+ self.assertEqual(
+ {'content-location': REQUEST_URL,
+ 'status': str(http_response.status),
+ 'xtra key': 'Xtra Value'},
+ response)
+
+
+class TestClosingProxyHttp(TestClosingHttp):
+
+ def closing_http(self, proxy_url=PROXY_URL, **kwargs):
+ connection = http.ClosingProxyHttp(proxy_url=proxy_url, **kwargs)
+ self.assertHasProxy(connection, proxy_url)
+ return connection
+
+ def test_class_without_proxy_url(self):
+ self.assertRaises(ValueError, http.ClosingProxyHttp, None)
+
+ def assertHasProxy(self, connection, proxy_url):
+ self.assertIsInstance(connection, http.ClosingProxyHttp)
+ proxy = connection.proxy
+ self.assertEqual(proxy_url,
+ '%s://%s:%i' % (proxy.scheme,
+ proxy.host,
+ proxy.port))