Test the Availability Zone [Profile] admin API
Depends-On: https://review.opendev.org/#/c/693765/ Change-Id: Ifc2965152c1bca8e899cf3c84153203ddb85712f
This commit is contained in:
parent
70c90ff66f
commit
c2aa20c24a
@ -17,6 +17,12 @@ from tempest import config
|
|||||||
|
|
||||||
from octavia_tempest_plugin.services.load_balancer.v2 import (
|
from octavia_tempest_plugin.services.load_balancer.v2 import (
|
||||||
amphora_client)
|
amphora_client)
|
||||||
|
from octavia_tempest_plugin.services.load_balancer.v2 import (
|
||||||
|
availability_zone_capabilities_client)
|
||||||
|
from octavia_tempest_plugin.services.load_balancer.v2 import (
|
||||||
|
availability_zone_client)
|
||||||
|
from octavia_tempest_plugin.services.load_balancer.v2 import (
|
||||||
|
availability_zone_profile_client)
|
||||||
from octavia_tempest_plugin.services.load_balancer.v2 import (
|
from octavia_tempest_plugin.services.load_balancer.v2 import (
|
||||||
flavor_capabilities_client)
|
flavor_capabilities_client)
|
||||||
from octavia_tempest_plugin.services.load_balancer.v2 import (
|
from octavia_tempest_plugin.services.load_balancer.v2 import (
|
||||||
@ -74,3 +80,11 @@ class ManagerV2(clients.Manager):
|
|||||||
self.provider_client = provider_client.ProviderClient(**params)
|
self.provider_client = provider_client.ProviderClient(**params)
|
||||||
self.flavor_capabilities_client = (
|
self.flavor_capabilities_client = (
|
||||||
flavor_capabilities_client.FlavorCapabilitiesClient(**params))
|
flavor_capabilities_client.FlavorCapabilitiesClient(**params))
|
||||||
|
self.availability_zone_capabilities_client = (
|
||||||
|
availability_zone_capabilities_client
|
||||||
|
.AvailabilityZoneCapabilitiesClient(**params))
|
||||||
|
self.availability_zone_profile_client = (
|
||||||
|
availability_zone_profile_client.AvailabilityZoneProfileClient(
|
||||||
|
**params))
|
||||||
|
self.availability_zone_client = (
|
||||||
|
availability_zone_client.AvailabilityZoneClient(**params))
|
||||||
|
@ -14,12 +14,17 @@
|
|||||||
|
|
||||||
# API field names
|
# API field names
|
||||||
ACTIVE_CONNECTIONS = 'active_connections'
|
ACTIVE_CONNECTIONS = 'active_connections'
|
||||||
|
AVAILABILITY_ZONE = 'availability_zone'
|
||||||
|
AVAILABILITY_ZONE_DATA = 'availability_zone_data'
|
||||||
|
AVAILABILITY_ZONE_PROFILE_ID = 'availability_zone_profile_id'
|
||||||
ADMIN_STATE_UP = 'admin_state_up'
|
ADMIN_STATE_UP = 'admin_state_up'
|
||||||
BYTES_IN = 'bytes_in'
|
BYTES_IN = 'bytes_in'
|
||||||
BYTES_OUT = 'bytes_out'
|
BYTES_OUT = 'bytes_out'
|
||||||
CREATED_AT = 'created_at'
|
CREATED_AT = 'created_at'
|
||||||
DESCRIPTION = 'description'
|
DESCRIPTION = 'description'
|
||||||
|
FLAVOR_DATA = 'flavor_data'
|
||||||
FLAVOR_ID = 'flavor_id'
|
FLAVOR_ID = 'flavor_id'
|
||||||
|
FLAVOR_PROFILE_ID = 'flavor_profile_id'
|
||||||
ID = 'id'
|
ID = 'id'
|
||||||
LISTENERS = 'listeners'
|
LISTENERS = 'listeners'
|
||||||
LOADBALANCER = 'loadbalancer'
|
LOADBALANCER = 'loadbalancer'
|
||||||
@ -78,9 +83,7 @@ HTTP_METHOD = 'http_method'
|
|||||||
URL_PATH = 'url_path'
|
URL_PATH = 'url_path'
|
||||||
EXPECTED_CODES = 'expected_codes'
|
EXPECTED_CODES = 'expected_codes'
|
||||||
|
|
||||||
FLAVOR_DATA = 'flavor_data'
|
|
||||||
ENABLED = 'enabled'
|
ENABLED = 'enabled'
|
||||||
FLAVOR_PROFILE_ID = 'flavor_profile_id'
|
|
||||||
|
|
||||||
# Other constants
|
# Other constants
|
||||||
ACTIVE = 'ACTIVE'
|
ACTIVE = 'ACTIVE'
|
||||||
@ -202,6 +205,10 @@ AMPHORA_PROVIDERS = ['amphora', 'amphorav2', 'octavia']
|
|||||||
# Flavor capabilities
|
# Flavor capabilities
|
||||||
LOADBALANCER_TOPOLOGY = 'loadbalancer_topology'
|
LOADBALANCER_TOPOLOGY = 'loadbalancer_topology'
|
||||||
|
|
||||||
|
# Availability zone capabilities
|
||||||
|
COMPUTE_ZONE = 'compute_zone'
|
||||||
|
MANAGEMENT_NETWORK = 'management_network'
|
||||||
|
|
||||||
# API valid fields
|
# API valid fields
|
||||||
SHOW_LOAD_BALANCER_RESPONSE_FIELDS = (
|
SHOW_LOAD_BALANCER_RESPONSE_FIELDS = (
|
||||||
ADMIN_STATE_UP, CREATED_AT, DESCRIPTION, FLAVOR_ID, ID, LISTENERS, NAME,
|
ADMIN_STATE_UP, CREATED_AT, DESCRIPTION, FLAVOR_ID, ID, LISTENERS, NAME,
|
||||||
@ -253,3 +260,9 @@ SHOW_AMPHORA_RESPONSE_FIELDS = [
|
|||||||
SHOW_FLAVOR_PROFILE_FIELDS = [ID, NAME, PROVIDER_NAME, FLAVOR_DATA]
|
SHOW_FLAVOR_PROFILE_FIELDS = [ID, NAME, PROVIDER_NAME, FLAVOR_DATA]
|
||||||
|
|
||||||
SHOW_FLAVOR_FIELDS = [ID, NAME, DESCRIPTION, ENABLED, FLAVOR_PROFILE_ID]
|
SHOW_FLAVOR_FIELDS = [ID, NAME, DESCRIPTION, ENABLED, FLAVOR_PROFILE_ID]
|
||||||
|
|
||||||
|
SHOW_AVAILABILITY_ZONE_PROFILE_FIELDS = [
|
||||||
|
ID, NAME, PROVIDER_NAME, AVAILABILITY_ZONE_DATA]
|
||||||
|
|
||||||
|
SHOW_AVAILABILITY_ZONE_FIELDS = [
|
||||||
|
NAME, DESCRIPTION, ENABLED, AVAILABILITY_ZONE_PROFILE_ID]
|
||||||
|
@ -132,6 +132,13 @@ OctaviaGroup = [
|
|||||||
'topology. One of: SINGLE - One amphora per load '
|
'topology. One of: SINGLE - One amphora per load '
|
||||||
'balancer. ACTIVE_STANDBY - Two amphora per load '
|
'balancer. ACTIVE_STANDBY - Two amphora per load '
|
||||||
'balancer.'}),
|
'balancer.'}),
|
||||||
|
cfg.DictOpt('expected_availability_zone_capability',
|
||||||
|
help=('Defines a provider availability zone capability that '
|
||||||
|
'is expected to be present in the selected provider '
|
||||||
|
'under test. It is specified in a "name": "description" '
|
||||||
|
'dict. Example: {"compute_zone": "The compute '
|
||||||
|
'availability zone."}'),
|
||||||
|
default={'compute_zone': 'The compute availability zone.'}),
|
||||||
# Networking
|
# Networking
|
||||||
cfg.BoolOpt('test_with_ipv6',
|
cfg.BoolOpt('test_with_ipv6',
|
||||||
default=True,
|
default=True,
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
# Copyright 2019 Rackspace US Inc. All rights reserved.
|
||||||
|
# Copyright 2019 Verizon Media
|
||||||
|
#
|
||||||
|
# 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 octavia_tempest_plugin.services.load_balancer.v2 import base_client
|
||||||
|
from octavia_tempest_plugin.services.load_balancer.v2 import provider_client
|
||||||
|
|
||||||
|
Unset = base_client.Unset
|
||||||
|
|
||||||
|
|
||||||
|
class AvailabilityZoneCapabilitiesClient(base_client.BaseLBaaSClient):
|
||||||
|
|
||||||
|
list_root_tag = 'availability_zone_capabilities'
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(AvailabilityZoneCapabilitiesClient, self).__init__(
|
||||||
|
*args, **kwargs)
|
||||||
|
providers_list_root_tag = provider_client.ProviderClient.list_root_tag
|
||||||
|
# /v2.0/lbaas/providers/<PROVIDER_UUID>/availability_zone_capabilities
|
||||||
|
self.uri = "{provider_base_uri}/{parent}/{object}".format(
|
||||||
|
provider_base_uri=self.base_uri.format(
|
||||||
|
object=providers_list_root_tag),
|
||||||
|
parent="{parent}",
|
||||||
|
object=self.list_root_tag
|
||||||
|
)
|
||||||
|
|
||||||
|
def list_availability_zone_capabilities(self, provider, query_params=None,
|
||||||
|
return_object_only=True):
|
||||||
|
"""Get a list of provider availability zone capability objects.
|
||||||
|
|
||||||
|
:param provider: The provider to query for availability zone
|
||||||
|
capabilities.
|
||||||
|
:param query_params: The optional query parameters to append to the
|
||||||
|
request. Ex. fields=id&fields=name
|
||||||
|
:param return_object_only: If True, the response returns the object
|
||||||
|
inside the root tag. False returns the full
|
||||||
|
response from the API.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotFound: If a 404 response code is received
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: A list of availability zone capability objects.
|
||||||
|
"""
|
||||||
|
return self._list_objects(parent_id=provider,
|
||||||
|
query_params=query_params,
|
||||||
|
return_object_only=return_object_only)
|
@ -0,0 +1,274 @@
|
|||||||
|
# Copyright 2019 Rackspace US Inc. 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.lib import exceptions
|
||||||
|
|
||||||
|
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
Unset = base_client.Unset
|
||||||
|
|
||||||
|
|
||||||
|
class AvailabilityZoneClient(base_client.BaseLBaaSClient):
|
||||||
|
|
||||||
|
root_tag = 'availability_zone'
|
||||||
|
list_root_tag = 'availability_zones'
|
||||||
|
|
||||||
|
resource_path = 'availabilityzones'
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(AvailabilityZoneClient, self).__init__(*args, **kwargs)
|
||||||
|
self.uri = self.base_uri.format(object=self.resource_path)
|
||||||
|
|
||||||
|
def create_availability_zone(self, name, availability_zone_profile_id,
|
||||||
|
description=Unset, enabled=Unset,
|
||||||
|
return_object_only=True):
|
||||||
|
"""Create an availability zone.
|
||||||
|
|
||||||
|
:param name: Human-readable name of the resource.
|
||||||
|
:param availability_zone_profile_id: The ID of the associated
|
||||||
|
availability zone profile.
|
||||||
|
:param description: A human-readable description for the resource.
|
||||||
|
:param enabled: If the resource is available for use.
|
||||||
|
The default is True.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotFound: If a 404 response code is received
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: An availability zone object.
|
||||||
|
"""
|
||||||
|
kwargs = {arg: value for arg, value in locals().items()
|
||||||
|
if arg != 'self' and value is not Unset}
|
||||||
|
return self._create_object(**kwargs)
|
||||||
|
|
||||||
|
def show_availability_zone(self, availability_zone_name, query_params=None,
|
||||||
|
return_object_only=True):
|
||||||
|
"""Get the availability zone details.
|
||||||
|
|
||||||
|
:param availability_zone_name: The availability zone name to query.
|
||||||
|
:param query_params: The optional query parameters to append to the
|
||||||
|
request. Ex. fields=id&fields=name
|
||||||
|
:param return_object_only: If True, the response returns the object
|
||||||
|
inside the root tag. False returns the full
|
||||||
|
response from the API.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotFound: If a 404 response code is received
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: An availability zone object.
|
||||||
|
"""
|
||||||
|
return self._show_object(obj_id=availability_zone_name,
|
||||||
|
query_params=query_params,
|
||||||
|
return_object_only=return_object_only)
|
||||||
|
|
||||||
|
def list_availability_zones(self, query_params=None,
|
||||||
|
return_object_only=True):
|
||||||
|
"""Get a list of availability zone objects.
|
||||||
|
|
||||||
|
:param query_params: The optional query parameters to append to the
|
||||||
|
request. Ex. fields=id&fields=name
|
||||||
|
:param return_object_only: If True, the response returns the object
|
||||||
|
inside the root tag. False returns the full
|
||||||
|
response from the API.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotFound: If a 404 response code is received
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: A list of availability zone objects.
|
||||||
|
"""
|
||||||
|
return self._list_objects(query_params=query_params,
|
||||||
|
return_object_only=return_object_only)
|
||||||
|
|
||||||
|
def update_availability_zone(self, availability_zone_name,
|
||||||
|
description=Unset, enabled=Unset,
|
||||||
|
return_object_only=True):
|
||||||
|
"""Update an availability zone.
|
||||||
|
|
||||||
|
:param availability_zone_name: The availability zone name to update.
|
||||||
|
:param description: A human-readable description for the resource.
|
||||||
|
:param enabled: If the resource is available for use.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotFound: If a 404 response code is received
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: An availability zone object.
|
||||||
|
"""
|
||||||
|
kwargs = {arg: value for arg, value in locals().items()
|
||||||
|
if arg != 'self' and value is not Unset}
|
||||||
|
kwargs['obj_id'] = kwargs.pop('availability_zone_name')
|
||||||
|
return self._update_object(**kwargs)
|
||||||
|
|
||||||
|
def delete_availability_zone(self, availability_zone_name,
|
||||||
|
ignore_errors=False):
|
||||||
|
"""Delete an availability zone.
|
||||||
|
|
||||||
|
:param availability_zone_name: The availability zone name to delete.
|
||||||
|
:param ignore_errors: True if errors should be ignored.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotFound: If a 404 response code is received
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: None if ignore_errors is True, the response status code
|
||||||
|
if not.
|
||||||
|
"""
|
||||||
|
return self._delete_obj(obj_id=availability_zone_name,
|
||||||
|
ignore_errors=ignore_errors)
|
||||||
|
|
||||||
|
def cleanup_an_availability_zone(self, availability_zone_name):
|
||||||
|
"""Delete an availability zone for tempest cleanup.
|
||||||
|
|
||||||
|
We cannot use the cleanup_availability_zone method as availability
|
||||||
|
zones do not have a provisioning_status.
|
||||||
|
|
||||||
|
:param availability_zone_name: The availability zone name to delete.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: None if ignore_errors is True, the response status code
|
||||||
|
if not.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._delete_obj(obj_id=availability_zone_name)
|
||||||
|
except exceptions.NotFound:
|
||||||
|
# Already gone, cleanup complete
|
||||||
|
LOG.info("Availability zone %s is already gone. "
|
||||||
|
"Cleanup considered complete.", availability_zone_name)
|
@ -0,0 +1,280 @@
|
|||||||
|
# Copyright 2019 Rackspace US Inc. 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.lib import exceptions
|
||||||
|
|
||||||
|
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
Unset = base_client.Unset
|
||||||
|
|
||||||
|
|
||||||
|
class AvailabilityZoneProfileClient(base_client.BaseLBaaSClient):
|
||||||
|
|
||||||
|
root_tag = 'availability_zone_profile'
|
||||||
|
list_root_tag = 'availability_zone_profiles'
|
||||||
|
|
||||||
|
resource_path = 'availabilityzoneprofiles'
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(AvailabilityZoneProfileClient, self).__init__(*args, **kwargs)
|
||||||
|
self.uri = self.base_uri.format(object=self.resource_path)
|
||||||
|
|
||||||
|
def create_availability_zone_profile(self, name, provider_name,
|
||||||
|
availability_zone_data,
|
||||||
|
return_object_only=True):
|
||||||
|
"""Create an availability zone profile.
|
||||||
|
|
||||||
|
:param name: Human-readable name of the resource.
|
||||||
|
:param provider_name: The octavia provider name.
|
||||||
|
:param availability_zone_data: The JSON string containing the
|
||||||
|
availability zone metadata.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotFound: If a 404 response code is received
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: An availability zone profile object.
|
||||||
|
"""
|
||||||
|
kwargs = {arg: value for arg, value in locals().items()
|
||||||
|
if arg != 'self' and value is not Unset}
|
||||||
|
return self._create_object(**kwargs)
|
||||||
|
|
||||||
|
def show_availability_zone_profile(self, availability_zone_profile_id,
|
||||||
|
query_params=None,
|
||||||
|
return_object_only=True):
|
||||||
|
"""Get the availability zone profile details.
|
||||||
|
|
||||||
|
:param availability_zone_profile_id: The availability zone profile ID
|
||||||
|
to query.
|
||||||
|
:param query_params: The optional query parameters to append to the
|
||||||
|
request. Ex. fields=id&fields=name
|
||||||
|
:param return_object_only: If True, the response returns the object
|
||||||
|
inside the root tag. False returns the full
|
||||||
|
response from the API.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotFound: If a 404 response code is received
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: An availability zone profile object.
|
||||||
|
"""
|
||||||
|
return self._show_object(obj_id=availability_zone_profile_id,
|
||||||
|
query_params=query_params,
|
||||||
|
return_object_only=return_object_only)
|
||||||
|
|
||||||
|
def list_availability_zone_profiles(self, query_params=None,
|
||||||
|
return_object_only=True):
|
||||||
|
"""Get a list of availability zone profile objects.
|
||||||
|
|
||||||
|
:param query_params: The optional query parameters to append to the
|
||||||
|
request. Ex. fields=id&fields=name
|
||||||
|
:param return_object_only: If True, the response returns the object
|
||||||
|
inside the root tag. False returns the full
|
||||||
|
response from the API.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotFound: If a 404 response code is received
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: A list of availability zone profile objects.
|
||||||
|
"""
|
||||||
|
return self._list_objects(query_params=query_params,
|
||||||
|
return_object_only=return_object_only)
|
||||||
|
|
||||||
|
def update_availability_zone_profile(
|
||||||
|
self, availability_zone_profile_id, name=Unset, provider_name=Unset,
|
||||||
|
availability_zone_data=Unset, return_object_only=True):
|
||||||
|
"""Update an availability zone profile.
|
||||||
|
|
||||||
|
:param availability_zone_profile_id: The availability zone profile ID
|
||||||
|
to update.
|
||||||
|
:param name: Human-readable name of the resource.
|
||||||
|
:param provider_name: The octavia provider name.
|
||||||
|
:param availability_zone_data: The JSON string containing the
|
||||||
|
availability zone metadata.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotFound: If a 404 response code is received
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: An availability zone profile object.
|
||||||
|
"""
|
||||||
|
kwargs = {arg: value for arg, value in locals().items()
|
||||||
|
if arg != 'self' and value is not Unset}
|
||||||
|
kwargs['obj_id'] = kwargs.pop('availability_zone_profile_id')
|
||||||
|
return self._update_object(**kwargs)
|
||||||
|
|
||||||
|
def delete_availability_zone_profile(self, availability_zone_profile_id,
|
||||||
|
ignore_errors=False):
|
||||||
|
"""Delete an availability zone profile.
|
||||||
|
|
||||||
|
:param availability_zone_profile_id: The availability zone profile ID
|
||||||
|
to delete.
|
||||||
|
:param ignore_errors: True if errors should be ignored.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotFound: If a 404 response code is received
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: None if ignore_errors is True, the response status code
|
||||||
|
if not.
|
||||||
|
"""
|
||||||
|
return self._delete_obj(obj_id=availability_zone_profile_id,
|
||||||
|
ignore_errors=ignore_errors)
|
||||||
|
|
||||||
|
def cleanup_availability_zone_profile(self, availability_zone_profile_id):
|
||||||
|
"""Delete an availability zone profile for tempest cleanup.
|
||||||
|
|
||||||
|
We cannot use the cleanup_availability_zone_profile method as
|
||||||
|
availability zone profiles do not have a provisioning_status.
|
||||||
|
|
||||||
|
:param availability_zone_profile_id: The availability zone profile ID
|
||||||
|
to delete.
|
||||||
|
:raises AssertionError: if the expected_code isn't a valid http success
|
||||||
|
response code
|
||||||
|
:raises BadRequest: If a 400 response code is received
|
||||||
|
:raises Conflict: If a 409 response code is received
|
||||||
|
:raises Forbidden: If a 403 response code is received
|
||||||
|
:raises Gone: If a 410 response code is received
|
||||||
|
:raises InvalidContentType: If a 415 response code is received
|
||||||
|
:raises InvalidHTTPResponseBody: The response body wasn't valid JSON
|
||||||
|
:raises InvalidHttpSuccessCode: if the read code isn't an expected
|
||||||
|
http success code
|
||||||
|
:raises NotImplemented: If a 501 response code is received
|
||||||
|
:raises OverLimit: If a 413 response code is received and over_limit is
|
||||||
|
not in the response body
|
||||||
|
:raises RateLimitExceeded: If a 413 response code is received and
|
||||||
|
over_limit is in the response body
|
||||||
|
:raises ServerFault: If a 500 response code is received
|
||||||
|
:raises Unauthorized: If a 401 response code is received
|
||||||
|
:raises UnexpectedContentType: If the content-type of the response
|
||||||
|
isn't an expect type
|
||||||
|
:raises UnexpectedResponseCode: If a response code above 400 is
|
||||||
|
received and it doesn't fall into any
|
||||||
|
of the handled checks
|
||||||
|
:raises UnprocessableEntity: If a 422 response code is received and
|
||||||
|
couldn't be parsed
|
||||||
|
:returns: None if ignore_errors is True, the response status code
|
||||||
|
if not.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._delete_obj(obj_id=availability_zone_profile_id)
|
||||||
|
except exceptions.NotFound:
|
||||||
|
# Already gone, cleanup complete
|
||||||
|
LOG.info("Availability zone profile %s is already gone. "
|
||||||
|
"Cleanup considered complete.",
|
||||||
|
availability_zone_profile_id)
|
492
octavia_tempest_plugin/tests/api/v2/test_availability_zone.py
Normal file
492
octavia_tempest_plugin/tests/api/v2/test_availability_zone.py
Normal file
@ -0,0 +1,492 @@
|
|||||||
|
# Copyright 2019 Rackspace US Inc. 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 copy
|
||||||
|
from operator import itemgetter
|
||||||
|
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
from tempest import config
|
||||||
|
from tempest.lib.common.utils import data_utils
|
||||||
|
from tempest.lib import decorators
|
||||||
|
from tempest.lib import exceptions
|
||||||
|
|
||||||
|
from octavia_tempest_plugin.common import constants as const
|
||||||
|
from octavia_tempest_plugin.tests import test_base
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class AvailabilityZoneAPITest(test_base.LoadBalancerBaseTest):
|
||||||
|
"""Test the availability zone object API."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def resource_setup(cls):
|
||||||
|
"""Setup resources needed by the tests."""
|
||||||
|
super(AvailabilityZoneAPITest, cls).resource_setup()
|
||||||
|
|
||||||
|
# We have to do this here as the api_version and clients are not
|
||||||
|
# setup in time to use a decorator or the skip_checks mixin
|
||||||
|
if not (cls.lb_admin_availability_zone_profile_client
|
||||||
|
.is_version_supported(cls.api_version, '2.14')):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create a shared availability zone profile
|
||||||
|
availability_zone_profile_name = data_utils.rand_name(
|
||||||
|
"lb_admin_availabilityzoneprofile-setup")
|
||||||
|
availability_zone_data = {
|
||||||
|
const.COMPUTE_ZONE: 'my_compute_zone',
|
||||||
|
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
|
||||||
|
}
|
||||||
|
availability_zone_data_json = jsonutils.dumps(availability_zone_data)
|
||||||
|
|
||||||
|
availability_zone_profile_kwargs = {
|
||||||
|
const.NAME: availability_zone_profile_name,
|
||||||
|
const.PROVIDER_NAME: CONF.load_balancer.provider,
|
||||||
|
const.AVAILABILITY_ZONE_DATA: availability_zone_data_json
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.availability_zone_profile = (
|
||||||
|
cls.lb_admin_availability_zone_profile_client
|
||||||
|
.create_availability_zone_profile(
|
||||||
|
**availability_zone_profile_kwargs))
|
||||||
|
cls.addClassResourceCleanup(
|
||||||
|
cls.lb_admin_availability_zone_profile_client
|
||||||
|
.cleanup_availability_zone_profile,
|
||||||
|
cls.availability_zone_profile[const.ID])
|
||||||
|
cls.availability_zone_profile_id = (
|
||||||
|
cls.availability_zone_profile[const.ID])
|
||||||
|
|
||||||
|
@decorators.idempotent_id('3899ef15-37c3-48a3-807f-8bb10bd295f0')
|
||||||
|
def test_availability_zone_create(self):
|
||||||
|
"""Tests availability zone create and basic show APIs.
|
||||||
|
|
||||||
|
* Tests that users without the loadbalancer admin role cannot
|
||||||
|
create an availability zone.
|
||||||
|
* Create a fully populated availability zone.
|
||||||
|
* Validate the response reflects the requested values.
|
||||||
|
"""
|
||||||
|
# We have to do this here as the api_version and clients are not
|
||||||
|
# setup in time to use a decorator or the skip_checks mixin
|
||||||
|
if not self.lb_admin_availability_zone_client.is_version_supported(
|
||||||
|
self.api_version, '2.14'):
|
||||||
|
raise self.skipException('Availability zones are only available '
|
||||||
|
'on Octavia API version 2.14 or newer.')
|
||||||
|
availability_zone_name = data_utils.rand_name(
|
||||||
|
"lb_admin_availability_zone-create")
|
||||||
|
availability_zone_description = data_utils.arbitrary_string(size=255)
|
||||||
|
|
||||||
|
availability_zone_kwargs = {
|
||||||
|
const.NAME: availability_zone_name,
|
||||||
|
const.DESCRIPTION: availability_zone_description,
|
||||||
|
const.ENABLED: True,
|
||||||
|
const.AVAILABILITY_ZONE_PROFILE_ID:
|
||||||
|
self.availability_zone_profile_id}
|
||||||
|
|
||||||
|
# Test that a user without the load balancer admin role cannot
|
||||||
|
# create an availability zone
|
||||||
|
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
|
||||||
|
self.assertRaises(exceptions.Forbidden,
|
||||||
|
self.os_primary.availability_zone_client
|
||||||
|
.create_availability_zone,
|
||||||
|
**availability_zone_kwargs)
|
||||||
|
|
||||||
|
# Happy path
|
||||||
|
availability_zone = (
|
||||||
|
self.lb_admin_availability_zone_client
|
||||||
|
.create_availability_zone(**availability_zone_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_client
|
||||||
|
.cleanup_an_availability_zone,
|
||||||
|
availability_zone[const.NAME])
|
||||||
|
|
||||||
|
self.assertEqual(availability_zone_name, availability_zone[const.NAME])
|
||||||
|
self.assertEqual(availability_zone_description,
|
||||||
|
availability_zone[const.DESCRIPTION])
|
||||||
|
self.assertTrue(availability_zone[const.ENABLED])
|
||||||
|
self.assertEqual(self.availability_zone_profile_id,
|
||||||
|
availability_zone[const.AVAILABILITY_ZONE_PROFILE_ID])
|
||||||
|
|
||||||
|
@decorators.idempotent_id('bba84c0c-2832-4c4c-90ff-d28acfe4ae36')
|
||||||
|
def test_availability_zone_list(self):
|
||||||
|
"""Tests availability zone list API and field filtering.
|
||||||
|
|
||||||
|
* Create three availability zones.
|
||||||
|
* Validates that non-admin accounts cannot list the availability zones.
|
||||||
|
* List the availability zones using the default sort order.
|
||||||
|
* List the availability zones using descending sort order.
|
||||||
|
* List the availability zones using ascending sort order.
|
||||||
|
* List the availability zones returning one field at a time.
|
||||||
|
* List the availability zones returning two fields.
|
||||||
|
* List the availability zones filtering to one of the three.
|
||||||
|
* List the availability zones filtered, one field, and sorted.
|
||||||
|
"""
|
||||||
|
# We have to do this here as the api_version and clients are not
|
||||||
|
# setup in time to use a decorator or the skip_checks mixin
|
||||||
|
if not self.lb_admin_availability_zone_client.is_version_supported(
|
||||||
|
self.api_version, '2.14'):
|
||||||
|
raise self.skipException('Availability zones are only available '
|
||||||
|
'on Octavia API version 2.14 or newer.')
|
||||||
|
|
||||||
|
# Create availability zone 1
|
||||||
|
az1_name = data_utils.rand_name("lb_admin_availability_zone-list-1")
|
||||||
|
az1_description = 'A'
|
||||||
|
|
||||||
|
az1_kwargs = {
|
||||||
|
const.NAME: az1_name,
|
||||||
|
const.DESCRIPTION: az1_description,
|
||||||
|
const.ENABLED: True,
|
||||||
|
const.AVAILABILITY_ZONE_PROFILE_ID:
|
||||||
|
self.availability_zone_profile_id}
|
||||||
|
|
||||||
|
az1 = (self.lb_admin_availability_zone_client
|
||||||
|
.create_availability_zone(**az1_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_client
|
||||||
|
.cleanup_an_availability_zone,
|
||||||
|
az1[const.NAME])
|
||||||
|
|
||||||
|
# Create availability zone 2
|
||||||
|
az2_name = data_utils.rand_name("lb_admin_availability_zone-list-2")
|
||||||
|
az2_description = 'B'
|
||||||
|
|
||||||
|
az2_kwargs = {
|
||||||
|
const.NAME: az2_name,
|
||||||
|
const.DESCRIPTION: az2_description,
|
||||||
|
const.ENABLED: False,
|
||||||
|
const.AVAILABILITY_ZONE_PROFILE_ID:
|
||||||
|
self.availability_zone_profile_id}
|
||||||
|
|
||||||
|
az2 = (self.lb_admin_availability_zone_client
|
||||||
|
.create_availability_zone(**az2_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_client
|
||||||
|
.cleanup_an_availability_zone,
|
||||||
|
az2[const.NAME])
|
||||||
|
|
||||||
|
# Create availability zone 3
|
||||||
|
az3_name = data_utils.rand_name("lb_admin_availability_zone-list-3")
|
||||||
|
az3_description = 'C'
|
||||||
|
|
||||||
|
az3_kwargs = {
|
||||||
|
const.NAME: az3_name,
|
||||||
|
const.DESCRIPTION: az3_description,
|
||||||
|
const.ENABLED: True,
|
||||||
|
const.AVAILABILITY_ZONE_PROFILE_ID:
|
||||||
|
self.availability_zone_profile_id}
|
||||||
|
|
||||||
|
az3 = (self.lb_admin_availability_zone_client
|
||||||
|
.create_availability_zone(**az3_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_client
|
||||||
|
.cleanup_an_availability_zone,
|
||||||
|
az3[const.NAME])
|
||||||
|
|
||||||
|
# default sort order (by Name) reference list
|
||||||
|
ref_id_list_asc = [az1[const.NAME], az2[const.NAME],
|
||||||
|
az3[const.NAME]]
|
||||||
|
ref_id_list_dsc = copy.deepcopy(ref_id_list_asc)
|
||||||
|
ref_id_list_asc.sort()
|
||||||
|
ref_id_list_dsc.sort(reverse=True)
|
||||||
|
|
||||||
|
# Test that a user without the load balancer role cannot
|
||||||
|
# list availability zones.
|
||||||
|
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.Forbidden,
|
||||||
|
self.os_primary.availability_zone_client
|
||||||
|
.list_availability_zones)
|
||||||
|
|
||||||
|
# Check the default sort order (by ID)
|
||||||
|
availability_zones = (
|
||||||
|
self.mem_availability_zone_client.list_availability_zones())
|
||||||
|
# Remove availability zones not used in this test
|
||||||
|
availability_zones = [
|
||||||
|
az for az in availability_zones
|
||||||
|
if 'lb_admin_availability_zone-list' in az[const.NAME]]
|
||||||
|
self.assertEqual(3, len(availability_zones))
|
||||||
|
self.assertEqual(ref_id_list_asc[0], availability_zones[0][const.NAME])
|
||||||
|
self.assertEqual(ref_id_list_asc[1], availability_zones[1][const.NAME])
|
||||||
|
self.assertEqual(ref_id_list_asc[2], availability_zones[2][const.NAME])
|
||||||
|
|
||||||
|
# Check the descending sort order by name
|
||||||
|
availability_zones = (
|
||||||
|
self.lb_admin_availability_zone_client.list_availability_zones(
|
||||||
|
query_params='{sort}={name}:{order}'.format(
|
||||||
|
sort=const.SORT, name=const.NAME, order=const.DESC)))
|
||||||
|
# Remove availability zones not used in this test
|
||||||
|
availability_zones = [
|
||||||
|
az for az in availability_zones
|
||||||
|
if 'lb_admin_availability_zone-list' in az[const.NAME]]
|
||||||
|
self.assertEqual(3, len(availability_zones))
|
||||||
|
self.assertEqual(az3_name, availability_zones[0][const.NAME])
|
||||||
|
self.assertEqual(az2_name, availability_zones[1][const.NAME])
|
||||||
|
self.assertEqual(az1_name, availability_zones[2][const.NAME])
|
||||||
|
|
||||||
|
# Check the ascending sort order by name
|
||||||
|
availability_zones = (
|
||||||
|
self.mem_availability_zone_client.list_availability_zones(
|
||||||
|
query_params='{sort}={name}:{order}'.format(
|
||||||
|
sort=const.SORT, name=const.NAME, order=const.ASC)))
|
||||||
|
# Remove availability zones not used in this test
|
||||||
|
availability_zones = [
|
||||||
|
az for az in availability_zones
|
||||||
|
if 'lb_admin_availability_zone-list' in az[const.NAME]]
|
||||||
|
self.assertEqual(3, len(availability_zones))
|
||||||
|
self.assertEqual(az1_name, availability_zones[0][const.NAME])
|
||||||
|
self.assertEqual(az2_name, availability_zones[1][const.NAME])
|
||||||
|
self.assertEqual(az3_name, availability_zones[2][const.NAME])
|
||||||
|
|
||||||
|
ref_availability_zones = [az1, az2, az3]
|
||||||
|
sorted_availability_zones = sorted(ref_availability_zones,
|
||||||
|
key=itemgetter(const.NAME))
|
||||||
|
sorted_enabled_availability_zones = [
|
||||||
|
az for az in sorted_availability_zones
|
||||||
|
if az[const.ENABLED]]
|
||||||
|
|
||||||
|
# Test fields
|
||||||
|
for field in const.SHOW_AVAILABILITY_ZONE_FIELDS:
|
||||||
|
availability_zones = (
|
||||||
|
self.mem_availability_zone_client
|
||||||
|
.list_availability_zones(
|
||||||
|
query_params='{fields}={field}&{fields}={name}'.format(
|
||||||
|
fields=const.FIELDS, field=field, name=const.NAME))
|
||||||
|
)
|
||||||
|
# Remove availability zones not used in this test
|
||||||
|
availability_zones = [
|
||||||
|
az for az in availability_zones
|
||||||
|
if 'lb_admin_availability_zone-list' in az[const.NAME]]
|
||||||
|
self.assertEqual(3, len(availability_zones))
|
||||||
|
self.assertEqual(sorted_availability_zones[0][field],
|
||||||
|
availability_zones[0][field])
|
||||||
|
self.assertEqual(sorted_availability_zones[1][field],
|
||||||
|
availability_zones[1][field])
|
||||||
|
self.assertEqual(sorted_availability_zones[2][field],
|
||||||
|
availability_zones[2][field])
|
||||||
|
|
||||||
|
# Test filtering
|
||||||
|
availability_zone = (
|
||||||
|
self.mem_availability_zone_client.list_availability_zones(
|
||||||
|
query_params='{name}={az_name}'.format(
|
||||||
|
name=const.NAME, az_name=az2[const.NAME])))
|
||||||
|
self.assertEqual(1, len(availability_zone))
|
||||||
|
self.assertEqual(az2[const.NAME], availability_zone[0][const.NAME])
|
||||||
|
|
||||||
|
# Test combined params
|
||||||
|
availability_zones = (
|
||||||
|
self.mem_availability_zone_client.list_availability_zones(
|
||||||
|
query_params='{enabled}={enable}&{fields}={name}&'
|
||||||
|
'{sort}={ID}:{desc}'.format(
|
||||||
|
enabled=const.ENABLED,
|
||||||
|
enable=True,
|
||||||
|
fields=const.FIELDS, name=const.NAME,
|
||||||
|
sort=const.SORT, ID=const.NAME,
|
||||||
|
desc=const.DESC)))
|
||||||
|
# Remove availability zones not used in this test
|
||||||
|
availability_zones = [
|
||||||
|
az for az in availability_zones
|
||||||
|
if 'lb_admin_availability_zone-list' in az[const.NAME]]
|
||||||
|
self.assertEqual(2, len(availability_zones))
|
||||||
|
self.assertEqual(1, len(availability_zones[0]))
|
||||||
|
self.assertEqual(sorted_enabled_availability_zones[1][const.NAME],
|
||||||
|
availability_zones[0][const.NAME])
|
||||||
|
self.assertEqual(sorted_enabled_availability_zones[0][const.NAME],
|
||||||
|
availability_zones[1][const.NAME])
|
||||||
|
|
||||||
|
@decorators.idempotent_id('4fa77f96-ba75-4255-bef8-6710cd7cb762')
|
||||||
|
def test_availability_zone_show(self):
|
||||||
|
"""Tests availability zone show API.
|
||||||
|
|
||||||
|
* Create a fully populated availability zone.
|
||||||
|
* Validate that non-lb-admin accounts cannot see the availability zone.
|
||||||
|
* Show availability zone details.
|
||||||
|
* Validate the show reflects the requested values.
|
||||||
|
"""
|
||||||
|
# We have to do this here as the api_version and clients are not
|
||||||
|
# setup in time to use a decorator or the skip_checks mixin
|
||||||
|
if not self.lb_admin_availability_zone_client.is_version_supported(
|
||||||
|
self.api_version, '2.14'):
|
||||||
|
raise self.skipException('Availability zones are only available '
|
||||||
|
'on Octavia API version 2.14 or newer.')
|
||||||
|
availability_zone_name = data_utils.rand_name(
|
||||||
|
"lb_admin_availability_zone-show")
|
||||||
|
availability_zone_description = data_utils.arbitrary_string(size=255)
|
||||||
|
|
||||||
|
availability_zone_kwargs = {
|
||||||
|
const.NAME: availability_zone_name,
|
||||||
|
const.DESCRIPTION: availability_zone_description,
|
||||||
|
const.ENABLED: True,
|
||||||
|
const.AVAILABILITY_ZONE_PROFILE_ID:
|
||||||
|
self.availability_zone_profile_id}
|
||||||
|
|
||||||
|
# Happy path
|
||||||
|
availability_zone = (
|
||||||
|
self.lb_admin_availability_zone_client
|
||||||
|
.create_availability_zone(**availability_zone_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_client
|
||||||
|
.cleanup_an_availability_zone,
|
||||||
|
availability_zone[const.NAME])
|
||||||
|
|
||||||
|
# Test that a user without the load balancer role cannot
|
||||||
|
# show availability zone details.
|
||||||
|
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.Forbidden,
|
||||||
|
self.os_primary.availability_zone_client
|
||||||
|
.show_availability_zone,
|
||||||
|
availability_zone[const.NAME])
|
||||||
|
|
||||||
|
result = self.mem_availability_zone_client.show_availability_zone(
|
||||||
|
availability_zone[const.NAME])
|
||||||
|
|
||||||
|
self.assertEqual(availability_zone_name, result[const.NAME])
|
||||||
|
self.assertEqual(availability_zone_description,
|
||||||
|
result[const.DESCRIPTION])
|
||||||
|
self.assertTrue(result[const.ENABLED])
|
||||||
|
self.assertEqual(self.availability_zone_profile_id,
|
||||||
|
result[const.AVAILABILITY_ZONE_PROFILE_ID])
|
||||||
|
|
||||||
|
@decorators.idempotent_id('9c466b9f-b70a-456d-9172-eb79b7820c7f')
|
||||||
|
def test_availability_zone_update(self):
|
||||||
|
"""Tests availability zone update API.
|
||||||
|
|
||||||
|
* Create a fully populated availability zone.
|
||||||
|
* Show availability zone details.
|
||||||
|
* Validate the show reflects the initial values.
|
||||||
|
* Validate that non-admin accounts cannot update the availability zone.
|
||||||
|
* Update the availability zone details.
|
||||||
|
* Show availability zone details.
|
||||||
|
* Validate the show reflects the updated values.
|
||||||
|
"""
|
||||||
|
# We have to do this here as the api_version and clients are not
|
||||||
|
# setup in time to use a decorator or the skip_checks mixin
|
||||||
|
if not self.lb_admin_availability_zone_client.is_version_supported(
|
||||||
|
self.api_version, '2.14'):
|
||||||
|
raise self.skipException('Availability zones are only available '
|
||||||
|
'on Octavia API version 2.14 or newer.')
|
||||||
|
availability_zone_name = data_utils.rand_name(
|
||||||
|
"lb_admin_availability_zone-update")
|
||||||
|
availability_zone_description = data_utils.arbitrary_string(size=255)
|
||||||
|
|
||||||
|
availability_zone_kwargs = {
|
||||||
|
const.NAME: availability_zone_name,
|
||||||
|
const.DESCRIPTION: availability_zone_description,
|
||||||
|
const.ENABLED: True,
|
||||||
|
const.AVAILABILITY_ZONE_PROFILE_ID:
|
||||||
|
self.availability_zone_profile_id}
|
||||||
|
|
||||||
|
# Happy path
|
||||||
|
availability_zone = (
|
||||||
|
self.lb_admin_availability_zone_client
|
||||||
|
.create_availability_zone(**availability_zone_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_client
|
||||||
|
.cleanup_an_availability_zone,
|
||||||
|
availability_zone[const.NAME])
|
||||||
|
|
||||||
|
availability_zone_description2 = data_utils.arbitrary_string(size=255)
|
||||||
|
availability_zone_updated_kwargs = {
|
||||||
|
const.DESCRIPTION: availability_zone_description2,
|
||||||
|
const.ENABLED: False}
|
||||||
|
|
||||||
|
# Test that a user without the load balancer role cannot
|
||||||
|
# show availability zone details.
|
||||||
|
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.Forbidden,
|
||||||
|
self.os_primary.availability_zone_client
|
||||||
|
.update_availability_zone,
|
||||||
|
availability_zone[const.NAME],
|
||||||
|
**availability_zone_updated_kwargs)
|
||||||
|
|
||||||
|
updated_availability_zone = (
|
||||||
|
self.lb_admin_availability_zone_client.update_availability_zone(
|
||||||
|
availability_zone[const.NAME],
|
||||||
|
**availability_zone_updated_kwargs))
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
availability_zone[const.NAME],
|
||||||
|
updated_availability_zone[const.NAME])
|
||||||
|
self.assertEqual(
|
||||||
|
availability_zone_description2,
|
||||||
|
updated_availability_zone[const.DESCRIPTION])
|
||||||
|
self.assertEqual(
|
||||||
|
availability_zone[const.AVAILABILITY_ZONE_PROFILE_ID],
|
||||||
|
updated_availability_zone[const.AVAILABILITY_ZONE_PROFILE_ID])
|
||||||
|
self.assertFalse(updated_availability_zone[const.ENABLED])
|
||||||
|
|
||||||
|
result = (
|
||||||
|
self.mem_availability_zone_client
|
||||||
|
.show_availability_zone(availability_zone[const.NAME]))
|
||||||
|
|
||||||
|
self.assertEqual(availability_zone[const.NAME], result[const.NAME])
|
||||||
|
self.assertEqual(availability_zone_description2,
|
||||||
|
result[const.DESCRIPTION])
|
||||||
|
self.assertEqual(availability_zone[const.AVAILABILITY_ZONE_PROFILE_ID],
|
||||||
|
result[const.AVAILABILITY_ZONE_PROFILE_ID])
|
||||||
|
self.assertFalse(result[const.ENABLED])
|
||||||
|
|
||||||
|
@decorators.idempotent_id('11585b33-2689-4693-be3b-26b210bb7fc5')
|
||||||
|
def test_availability_zone_delete(self):
|
||||||
|
"""Tests availability zone create and delete APIs.
|
||||||
|
|
||||||
|
* Creates an availability zone.
|
||||||
|
* Validates that other accounts cannot delete the availability zone.
|
||||||
|
* Deletes the availability zone.
|
||||||
|
* Validates the availability zone no longer exists.
|
||||||
|
"""
|
||||||
|
# We have to do this here as the api_version and clients are not
|
||||||
|
# setup in time to use a decorator or the skip_checks mixin
|
||||||
|
if not self.lb_admin_availability_zone_client.is_version_supported(
|
||||||
|
self.api_version, '2.14'):
|
||||||
|
raise self.skipException('Availability zones are only available '
|
||||||
|
'on Octavia API version 2.14 or newer.')
|
||||||
|
availability_zone_name = data_utils.rand_name(
|
||||||
|
"lb_admin_availability_zone-delete")
|
||||||
|
availability_zone_description = data_utils.arbitrary_string(size=255)
|
||||||
|
|
||||||
|
availability_zone_kwargs = {
|
||||||
|
const.NAME: availability_zone_name,
|
||||||
|
const.DESCRIPTION: availability_zone_description,
|
||||||
|
const.ENABLED: True,
|
||||||
|
const.AVAILABILITY_ZONE_PROFILE_ID:
|
||||||
|
self.availability_zone_profile_id}
|
||||||
|
|
||||||
|
# Happy path
|
||||||
|
availability_zone = (
|
||||||
|
self.lb_admin_availability_zone_client
|
||||||
|
.create_availability_zone(**availability_zone_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_client
|
||||||
|
.cleanup_an_availability_zone,
|
||||||
|
availability_zone[const.NAME])
|
||||||
|
|
||||||
|
# Test that a user without the load balancer admin role cannot
|
||||||
|
# delete an availability zone.
|
||||||
|
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.Forbidden,
|
||||||
|
self.os_primary.availability_zone_client
|
||||||
|
.delete_availability_zone,
|
||||||
|
availability_zone[const.NAME])
|
||||||
|
|
||||||
|
# Happy path
|
||||||
|
self.lb_admin_availability_zone_client.delete_availability_zone(
|
||||||
|
availability_zone[const.NAME])
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.NotFound,
|
||||||
|
self.lb_admin_availability_zone_client.show_availability_zone,
|
||||||
|
availability_zone[const.NAME])
|
@ -0,0 +1,93 @@
|
|||||||
|
# Copyright 2019 Rackspace US Inc. All rights reserved.
|
||||||
|
# Copyright 2019 Verizon Media
|
||||||
|
#
|
||||||
|
# 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 import config
|
||||||
|
from tempest.lib import decorators
|
||||||
|
from tempest.lib import exceptions
|
||||||
|
|
||||||
|
from octavia_tempest_plugin.common import constants as const
|
||||||
|
from octavia_tempest_plugin.tests import test_base
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class AvailabilityZoneCapabilitiesAPITest(test_base.LoadBalancerBaseTest):
|
||||||
|
"""Test the provider availability zone capabilities API."""
|
||||||
|
|
||||||
|
@decorators.idempotent_id('cb3e4c59-4114-420b-9837-2666d4d5fef4')
|
||||||
|
def test_availability_zone_capabilities_list(self):
|
||||||
|
"""Tests provider availability zone capabilities list API/filtering.
|
||||||
|
|
||||||
|
* Validates that non-lb admin accounts cannot list the capabilities.
|
||||||
|
* List the availability zone capablilities.
|
||||||
|
* Validate that the "loadbalancer_topology" capablility is present.
|
||||||
|
* List the providers returning one field at a time.
|
||||||
|
"""
|
||||||
|
# We have to do this here as the api_version and clients are not
|
||||||
|
# setup in time to use a decorator or the skip_checks mixin
|
||||||
|
if not self.mem_provider_client.is_version_supported(
|
||||||
|
self.api_version, '2.14'):
|
||||||
|
raise self.skipException(
|
||||||
|
'Availability zone capabilities are only available '
|
||||||
|
'on Octavia API version 2.14 or newer.')
|
||||||
|
|
||||||
|
# Test that a user without the load balancer admin role cannot
|
||||||
|
# list provider availability zone capabilities.
|
||||||
|
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
|
||||||
|
os_primary_capabilities_client = (
|
||||||
|
self.os_primary.availability_zone_capabilities_client)
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.Forbidden,
|
||||||
|
(os_primary_capabilities_client
|
||||||
|
.list_availability_zone_capabilities),
|
||||||
|
CONF.load_balancer.provider)
|
||||||
|
|
||||||
|
# Check for an expected availability zone capability for the
|
||||||
|
# configured provider
|
||||||
|
admin_capabilities_client = (
|
||||||
|
self.lb_admin_availability_zone_capabilities_client)
|
||||||
|
capabilities = (
|
||||||
|
admin_capabilities_client.list_availability_zone_capabilities(
|
||||||
|
CONF.load_balancer.provider))
|
||||||
|
|
||||||
|
expected_name = list(
|
||||||
|
CONF.load_balancer.expected_availability_zone_capability)[0]
|
||||||
|
expected_description = (
|
||||||
|
CONF.load_balancer.expected_availability_zone_capability[
|
||||||
|
expected_name])
|
||||||
|
for capability in capabilities:
|
||||||
|
if capability[const.NAME] == expected_name:
|
||||||
|
self.assertEqual(expected_description,
|
||||||
|
capability[const.DESCRIPTION])
|
||||||
|
|
||||||
|
# Test fields
|
||||||
|
capabilities = (
|
||||||
|
admin_capabilities_client.list_availability_zone_capabilities(
|
||||||
|
CONF.load_balancer.provider,
|
||||||
|
query_params='{fields}={field}&{field}={exp_name}'.format(
|
||||||
|
fields=const.FIELDS, field=const.NAME,
|
||||||
|
exp_name=expected_name)))
|
||||||
|
self.assertEqual(1, len(capabilities[0]))
|
||||||
|
self.assertEqual(expected_name, capabilities[0][const.NAME])
|
||||||
|
|
||||||
|
capabilities = (
|
||||||
|
admin_capabilities_client.list_availability_zone_capabilities(
|
||||||
|
CONF.load_balancer.provider,
|
||||||
|
query_params='{fields}={field}&{name}={exp_name}'.format(
|
||||||
|
fields=const.FIELDS, field=const.DESCRIPTION,
|
||||||
|
name=const.NAME, exp_name=expected_name)))
|
||||||
|
self.assertEqual(1, len(capabilities[0]))
|
||||||
|
self.assertEqual(expected_description,
|
||||||
|
capabilities[0][const.DESCRIPTION])
|
@ -0,0 +1,536 @@
|
|||||||
|
# Copyright 2019 Rackspace US Inc. 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 copy
|
||||||
|
from operator import itemgetter
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
from tempest import config
|
||||||
|
from tempest.lib.common.utils import data_utils
|
||||||
|
from tempest.lib import decorators
|
||||||
|
from tempest.lib import exceptions
|
||||||
|
|
||||||
|
from octavia_tempest_plugin.common import constants as const
|
||||||
|
from octavia_tempest_plugin.tests import test_base
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class AvailabilityZoneProfileAPITest(test_base.LoadBalancerBaseTest):
|
||||||
|
"""Test the availability zone profile object API."""
|
||||||
|
|
||||||
|
@decorators.idempotent_id('e512b580-ef32-44c3-bbd2-efdc27ba2ea6')
|
||||||
|
def test_availability_zone_profile_create(self):
|
||||||
|
"""Tests availability zone profile create and basic show APIs.
|
||||||
|
|
||||||
|
* Tests that users without the loadbalancer admin role cannot
|
||||||
|
create availability zone profiles.
|
||||||
|
* Create a fully populated availability zone profile.
|
||||||
|
* Validate the response reflects the requested values.
|
||||||
|
"""
|
||||||
|
# We have to do this here as the api_version and clients are not
|
||||||
|
# setup in time to use a decorator or the skip_checks mixin
|
||||||
|
if not (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.is_version_supported(self.api_version, '2.14')):
|
||||||
|
raise self.skipException(
|
||||||
|
'Availability zone profiles are only available on '
|
||||||
|
'Octavia API version 2.14 or newer.')
|
||||||
|
|
||||||
|
availability_zone_profile_name = data_utils.rand_name(
|
||||||
|
"lb_admin_availabilityzoneprofile1-create")
|
||||||
|
availability_zone_data = {
|
||||||
|
const.COMPUTE_ZONE: 'my_compute_zone',
|
||||||
|
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
|
||||||
|
}
|
||||||
|
availability_zone_data_json = jsonutils.dumps(availability_zone_data)
|
||||||
|
|
||||||
|
availability_zone_profile_kwargs = {
|
||||||
|
const.NAME: availability_zone_profile_name,
|
||||||
|
const.PROVIDER_NAME: CONF.load_balancer.provider,
|
||||||
|
const.AVAILABILITY_ZONE_DATA: availability_zone_data_json
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test that a user without the load balancer admin role cannot
|
||||||
|
# create an availability zone profile profile
|
||||||
|
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.Forbidden,
|
||||||
|
self.os_primary.availability_zone_profile_client
|
||||||
|
.create_availability_zone_profile,
|
||||||
|
**availability_zone_profile_kwargs)
|
||||||
|
|
||||||
|
# Happy path
|
||||||
|
availability_zone_profile = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.create_availability_zone_profile(
|
||||||
|
**availability_zone_profile_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.cleanup_availability_zone_profile,
|
||||||
|
availability_zone_profile[const.ID])
|
||||||
|
|
||||||
|
UUID(availability_zone_profile[const.ID])
|
||||||
|
self.assertEqual(
|
||||||
|
availability_zone_profile_name,
|
||||||
|
availability_zone_profile[const.NAME])
|
||||||
|
self.assertEqual(
|
||||||
|
CONF.load_balancer.provider,
|
||||||
|
availability_zone_profile[const.PROVIDER_NAME])
|
||||||
|
self.assertEqual(
|
||||||
|
availability_zone_data_json,
|
||||||
|
availability_zone_profile[const.AVAILABILITY_ZONE_DATA])
|
||||||
|
|
||||||
|
@decorators.idempotent_id('ef7d1c45-e312-46ce-8dcb-f2fe26295658')
|
||||||
|
def test_availability_zone_profile_list(self):
|
||||||
|
"""Tests availability zone profile list API and field filtering.
|
||||||
|
|
||||||
|
* Create three availability zone profiles.
|
||||||
|
* Validates that non-admin accounts cannot list the availability zone
|
||||||
|
profiles.
|
||||||
|
* List the availability zone profiles using the default sort order.
|
||||||
|
* List the availability zone profiles using descending sort order.
|
||||||
|
* List the availability zone profiles using ascending sort order.
|
||||||
|
* List the availability zone profiles returning one field at a time.
|
||||||
|
* List the availability zone profiles returning two fields.
|
||||||
|
* List the availability zone profiles filtering to one of the three.
|
||||||
|
* List the availability zone profiles filtered, one field, and sorted.
|
||||||
|
"""
|
||||||
|
# We have to do this here as the api_version and clients are not
|
||||||
|
# setup in time to use a decorator or the skip_checks mixin
|
||||||
|
if not (self.lb_admin_availability_zone_profile_client
|
||||||
|
.is_version_supported(self.api_version, '2.14')):
|
||||||
|
raise self.skipException(
|
||||||
|
'Availability zone profiles are only available on '
|
||||||
|
'Octavia API version 2.14 or newer.')
|
||||||
|
|
||||||
|
# Create availability zone profile 1
|
||||||
|
availability_zone_profile1_name = data_utils.rand_name(
|
||||||
|
"lb_admin_availabilityzoneprofile-list-1")
|
||||||
|
availability_zone_data1 = {
|
||||||
|
const.COMPUTE_ZONE: 'my_compute_zone1',
|
||||||
|
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
|
||||||
|
}
|
||||||
|
availability_zone_data1_json = jsonutils.dumps(availability_zone_data1)
|
||||||
|
|
||||||
|
availability_zone_profile1_kwargs = {
|
||||||
|
const.NAME: availability_zone_profile1_name,
|
||||||
|
const.PROVIDER_NAME: CONF.load_balancer.provider,
|
||||||
|
const.AVAILABILITY_ZONE_DATA: availability_zone_data1_json
|
||||||
|
}
|
||||||
|
availability_zone_profile1 = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.create_availability_zone_profile(
|
||||||
|
**availability_zone_profile1_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.cleanup_availability_zone_profile,
|
||||||
|
availability_zone_profile1[const.ID])
|
||||||
|
|
||||||
|
# Create availability zone profile 2
|
||||||
|
availability_zone_profile2_name = data_utils.rand_name(
|
||||||
|
"lb_admin_availabilityzoneprofile-list-2")
|
||||||
|
availability_zone_data2 = {
|
||||||
|
const.COMPUTE_ZONE: 'my_compute_zone2',
|
||||||
|
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
|
||||||
|
}
|
||||||
|
availability_zone_data2_json = jsonutils.dumps(availability_zone_data2)
|
||||||
|
|
||||||
|
availability_zone_profile2_kwargs = {
|
||||||
|
const.NAME: availability_zone_profile2_name,
|
||||||
|
const.PROVIDER_NAME: CONF.load_balancer.provider,
|
||||||
|
const.AVAILABILITY_ZONE_DATA: availability_zone_data2_json
|
||||||
|
}
|
||||||
|
availability_zone_profile2 = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.create_availability_zone_profile(
|
||||||
|
**availability_zone_profile2_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.cleanup_availability_zone_profile,
|
||||||
|
availability_zone_profile2[const.ID])
|
||||||
|
|
||||||
|
# Create availability zone profile 3
|
||||||
|
availability_zone_profile3_name = data_utils.rand_name(
|
||||||
|
"lb_admin_availabilityzoneprofile-list-3")
|
||||||
|
availability_zone_data3 = {
|
||||||
|
const.COMPUTE_ZONE: 'my_compute_zone3',
|
||||||
|
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
|
||||||
|
}
|
||||||
|
availability_zone_data3_json = jsonutils.dumps(availability_zone_data3)
|
||||||
|
|
||||||
|
availability_zone_profile3_kwargs = {
|
||||||
|
const.NAME: availability_zone_profile3_name,
|
||||||
|
const.PROVIDER_NAME: CONF.load_balancer.provider,
|
||||||
|
const.AVAILABILITY_ZONE_DATA: availability_zone_data3_json
|
||||||
|
}
|
||||||
|
availability_zone_profile3 = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.create_availability_zone_profile(
|
||||||
|
**availability_zone_profile3_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.cleanup_availability_zone_profile,
|
||||||
|
availability_zone_profile3[const.ID])
|
||||||
|
|
||||||
|
# default sort order (by ID) reference list
|
||||||
|
ref_id_list_asc = [availability_zone_profile1[const.ID],
|
||||||
|
availability_zone_profile2[const.ID],
|
||||||
|
availability_zone_profile3[const.ID]]
|
||||||
|
ref_id_list_dsc = copy.deepcopy(ref_id_list_asc)
|
||||||
|
ref_id_list_asc.sort()
|
||||||
|
ref_id_list_dsc.sort(reverse=True)
|
||||||
|
|
||||||
|
# Test that a user without the load balancer admin role cannot
|
||||||
|
# list availability zone profiles.
|
||||||
|
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.Forbidden,
|
||||||
|
self.os_primary.availability_zone_profile_client
|
||||||
|
.list_availability_zone_profiles)
|
||||||
|
|
||||||
|
# Check the default sort order (by ID)
|
||||||
|
profiles = (self.lb_admin_availability_zone_profile_client
|
||||||
|
.list_availability_zone_profiles())
|
||||||
|
# Remove availability zone profiles not used in this test
|
||||||
|
profiles = [
|
||||||
|
prof for prof in profiles
|
||||||
|
if 'lb_admin_availabilityzoneprofile-list' in prof[const.NAME]]
|
||||||
|
self.assertEqual(3, len(profiles))
|
||||||
|
self.assertEqual(ref_id_list_asc[0], profiles[0][const.ID])
|
||||||
|
self.assertEqual(ref_id_list_asc[1], profiles[1][const.ID])
|
||||||
|
self.assertEqual(ref_id_list_asc[2], profiles[2][const.ID])
|
||||||
|
|
||||||
|
# Check the descending sort order by name
|
||||||
|
profiles = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.list_availability_zone_profiles(
|
||||||
|
query_params='{sort}={name}:{order}'.format(
|
||||||
|
sort=const.SORT, name=const.NAME, order=const.DESC)))
|
||||||
|
# Remove availability zone profiles not used in this test
|
||||||
|
profiles = [
|
||||||
|
prof for prof in profiles
|
||||||
|
if 'lb_admin_availabilityzoneprofile-list' in prof[const.NAME]]
|
||||||
|
self.assertEqual(3, len(profiles))
|
||||||
|
self.assertEqual(availability_zone_profile3_name,
|
||||||
|
profiles[0][const.NAME])
|
||||||
|
self.assertEqual(availability_zone_profile2_name,
|
||||||
|
profiles[1][const.NAME])
|
||||||
|
self.assertEqual(availability_zone_profile1_name,
|
||||||
|
profiles[2][const.NAME])
|
||||||
|
|
||||||
|
# Check the ascending sort order by name
|
||||||
|
profiles = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.list_availability_zone_profiles(
|
||||||
|
query_params='{sort}={name}:{order}'.format(
|
||||||
|
sort=const.SORT, name=const.NAME, order=const.ASC)))
|
||||||
|
# Remove availability zone profiles not used in this test
|
||||||
|
profiles = [
|
||||||
|
prof for prof in profiles
|
||||||
|
if 'lb_admin_availabilityzoneprofile-list' in prof[const.NAME]]
|
||||||
|
self.assertEqual(3, len(profiles))
|
||||||
|
self.assertEqual(availability_zone_profile1_name,
|
||||||
|
profiles[0][const.NAME])
|
||||||
|
self.assertEqual(availability_zone_profile2_name,
|
||||||
|
profiles[1][const.NAME])
|
||||||
|
self.assertEqual(availability_zone_profile3_name,
|
||||||
|
profiles[2][const.NAME])
|
||||||
|
|
||||||
|
ref_profiles = [availability_zone_profile1, availability_zone_profile2,
|
||||||
|
availability_zone_profile3]
|
||||||
|
sorted_profiles = sorted(ref_profiles, key=itemgetter(const.ID))
|
||||||
|
|
||||||
|
# Test fields
|
||||||
|
availability_zone_profile_client = (
|
||||||
|
self.lb_admin_availability_zone_profile_client)
|
||||||
|
for field in const.SHOW_AVAILABILITY_ZONE_PROFILE_FIELDS:
|
||||||
|
profiles = (
|
||||||
|
availability_zone_profile_client
|
||||||
|
.list_availability_zone_profiles(
|
||||||
|
query_params='{fields}={field}&{fields}={name}'.format(
|
||||||
|
fields=const.FIELDS, field=field, name=const.NAME)))
|
||||||
|
# Remove availability zone profiles not used in this test
|
||||||
|
profiles = [
|
||||||
|
prof for prof in profiles
|
||||||
|
if 'lb_admin_availabilityzoneprofile-list' in prof[const.NAME]]
|
||||||
|
|
||||||
|
self.assertEqual(3, len(profiles))
|
||||||
|
self.assertEqual(sorted_profiles[0][field], profiles[0][field])
|
||||||
|
self.assertEqual(sorted_profiles[1][field], profiles[1][field])
|
||||||
|
self.assertEqual(sorted_profiles[2][field], profiles[2][field])
|
||||||
|
|
||||||
|
# Test filtering
|
||||||
|
profile = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.list_availability_zone_profiles(
|
||||||
|
query_params='{name}={prof_name}'.format(
|
||||||
|
name=const.NAME,
|
||||||
|
prof_name=availability_zone_profile2[const.NAME])))
|
||||||
|
self.assertEqual(1, len(profile))
|
||||||
|
self.assertEqual(availability_zone_profile2[const.ID],
|
||||||
|
profile[0][const.ID])
|
||||||
|
|
||||||
|
# Test combined params
|
||||||
|
profiles = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.list_availability_zone_profiles(
|
||||||
|
query_params='{provider_name}={provider}&{fields}={name}&'
|
||||||
|
'{sort}={ID}:{desc}'.format(
|
||||||
|
provider_name=const.PROVIDER_NAME,
|
||||||
|
provider=CONF.load_balancer.provider,
|
||||||
|
fields=const.FIELDS, name=const.NAME,
|
||||||
|
sort=const.SORT, ID=const.ID,
|
||||||
|
desc=const.DESC)))
|
||||||
|
# Remove availability zone profiles not used in this test
|
||||||
|
profiles = [
|
||||||
|
prof for prof in profiles
|
||||||
|
if 'lb_admin_availabilityzoneprofile-list' in prof[const.NAME]]
|
||||||
|
self.assertEqual(3, len(profiles))
|
||||||
|
self.assertEqual(1, len(profiles[0]))
|
||||||
|
self.assertEqual(sorted_profiles[2][const.NAME],
|
||||||
|
profiles[0][const.NAME])
|
||||||
|
self.assertEqual(sorted_profiles[1][const.NAME],
|
||||||
|
profiles[1][const.NAME])
|
||||||
|
self.assertEqual(sorted_profiles[0][const.NAME],
|
||||||
|
profiles[2][const.NAME])
|
||||||
|
|
||||||
|
@decorators.idempotent_id('379d92dc-7f6d-4674-ae6f-b3aa2120c677')
|
||||||
|
def test_availability_zone_profile_show(self):
|
||||||
|
"""Tests availability zone profile show API.
|
||||||
|
|
||||||
|
* Create a fully populated availability zone profile.
|
||||||
|
* Show availability zone profile details.
|
||||||
|
* Validate the show reflects the requested values.
|
||||||
|
* Validates that non-lb-admin accounts cannot see the availability zone
|
||||||
|
profile.
|
||||||
|
"""
|
||||||
|
# We have to do this here as the api_version and clients are not
|
||||||
|
# setup in time to use a decorator or the skip_checks mixin
|
||||||
|
if not (self.lb_admin_availability_zone_profile_client
|
||||||
|
.is_version_supported(self.api_version, '2.14')):
|
||||||
|
raise self.skipException(
|
||||||
|
'Availability zone profiles are only available on '
|
||||||
|
'Octavia API version 2.14 or newer.')
|
||||||
|
|
||||||
|
availability_zone_profile_name = data_utils.rand_name(
|
||||||
|
"lb_admin_availabilityzoneprofile1-show")
|
||||||
|
availability_zone_data = {
|
||||||
|
const.COMPUTE_ZONE: 'my_compute_zone',
|
||||||
|
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
|
||||||
|
}
|
||||||
|
availability_zone_data_json = jsonutils.dumps(availability_zone_data)
|
||||||
|
|
||||||
|
availability_zone_profile_kwargs = {
|
||||||
|
const.NAME: availability_zone_profile_name,
|
||||||
|
const.PROVIDER_NAME: CONF.load_balancer.provider,
|
||||||
|
const.AVAILABILITY_ZONE_DATA: availability_zone_data_json
|
||||||
|
}
|
||||||
|
|
||||||
|
availability_zone_profile = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.create_availability_zone_profile(
|
||||||
|
**availability_zone_profile_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.cleanup_availability_zone_profile,
|
||||||
|
availability_zone_profile[const.ID])
|
||||||
|
|
||||||
|
# Test that a user without the load balancer admin role cannot
|
||||||
|
# show an availability zone profile profile
|
||||||
|
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.Forbidden,
|
||||||
|
self.os_primary.availability_zone_profile_client
|
||||||
|
.show_availability_zone_profile,
|
||||||
|
availability_zone_profile[const.ID])
|
||||||
|
|
||||||
|
result = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.show_availability_zone_profile(
|
||||||
|
availability_zone_profile[const.ID]))
|
||||||
|
|
||||||
|
self.assertEqual(availability_zone_profile_name, result[const.NAME])
|
||||||
|
self.assertEqual(CONF.load_balancer.provider,
|
||||||
|
result[const.PROVIDER_NAME])
|
||||||
|
self.assertEqual(availability_zone_data_json,
|
||||||
|
result[const.AVAILABILITY_ZONE_DATA])
|
||||||
|
|
||||||
|
@decorators.idempotent_id('7121d4c0-f751-4b4e-a4c1-ab06c27a54a4')
|
||||||
|
def test_availability_zone_profile_update(self):
|
||||||
|
"""Tests availability zone profile update API.
|
||||||
|
|
||||||
|
* Create a fully populated availability zone profile.
|
||||||
|
* Show availability zone profile details.
|
||||||
|
* Validate the show reflects the initial values.
|
||||||
|
* Validates that non-admin accounts cannot update the availability zone
|
||||||
|
profile.
|
||||||
|
* Update the availability zone profile details.
|
||||||
|
* Show availability zone profile details.
|
||||||
|
* Validate the show reflects the updated values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# We have to do this here as the api_version and clients are not
|
||||||
|
# setup in time to use a decorator or the skip_checks mixin
|
||||||
|
if not (self.lb_admin_availability_zone_profile_client
|
||||||
|
.is_version_supported(self.api_version, '2.14')):
|
||||||
|
raise self.skipException(
|
||||||
|
'Availability zone profiles are only available on '
|
||||||
|
'Octavia API version 2.14 or newer.')
|
||||||
|
|
||||||
|
availability_zone_profile_name = data_utils.rand_name(
|
||||||
|
"lb_admin_availabilityzoneprofile1-update")
|
||||||
|
availability_zone_data = {
|
||||||
|
const.COMPUTE_ZONE: 'my_compute_zone1',
|
||||||
|
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
|
||||||
|
}
|
||||||
|
availability_zone_data_json = jsonutils.dumps(availability_zone_data)
|
||||||
|
|
||||||
|
availability_zone_profile_kwargs = {
|
||||||
|
const.NAME: availability_zone_profile_name,
|
||||||
|
const.PROVIDER_NAME: CONF.load_balancer.provider,
|
||||||
|
const.AVAILABILITY_ZONE_DATA: availability_zone_data_json
|
||||||
|
}
|
||||||
|
|
||||||
|
availability_zone_profile = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.create_availability_zone_profile(
|
||||||
|
**availability_zone_profile_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.cleanup_availability_zone_profile,
|
||||||
|
availability_zone_profile[const.ID])
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
availability_zone_profile_name,
|
||||||
|
availability_zone_profile[const.NAME])
|
||||||
|
self.assertEqual(
|
||||||
|
CONF.load_balancer.provider,
|
||||||
|
availability_zone_profile[const.PROVIDER_NAME])
|
||||||
|
self.assertEqual(
|
||||||
|
availability_zone_data_json,
|
||||||
|
availability_zone_profile[const.AVAILABILITY_ZONE_DATA])
|
||||||
|
|
||||||
|
availability_zone_profile_name2 = data_utils.rand_name(
|
||||||
|
"lb_admin_availabilityzoneprofile1-update2")
|
||||||
|
availability_zone_data2 = {
|
||||||
|
const.COMPUTE_ZONE: 'my_compute_zone2',
|
||||||
|
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
|
||||||
|
}
|
||||||
|
availability_zone_data2_json = jsonutils.dumps(availability_zone_data2)
|
||||||
|
|
||||||
|
# TODO(johnsom) Figure out a reliable second provider
|
||||||
|
availability_zone_profile_updated_kwargs = {
|
||||||
|
const.NAME: availability_zone_profile_name2,
|
||||||
|
const.PROVIDER_NAME: CONF.load_balancer.provider,
|
||||||
|
const.AVAILABILITY_ZONE_DATA: availability_zone_data2_json
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test that a user without the load balancer admin role cannot
|
||||||
|
# create an availability zone profile profile
|
||||||
|
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.Forbidden,
|
||||||
|
self.os_primary.availability_zone_profile_client
|
||||||
|
.update_availability_zone_profile,
|
||||||
|
availability_zone_profile[const.ID],
|
||||||
|
**availability_zone_profile_updated_kwargs)
|
||||||
|
|
||||||
|
result = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.update_availability_zone_profile(
|
||||||
|
availability_zone_profile[const.ID],
|
||||||
|
**availability_zone_profile_updated_kwargs))
|
||||||
|
|
||||||
|
self.assertEqual(availability_zone_profile_name2, result[const.NAME])
|
||||||
|
self.assertEqual(CONF.load_balancer.provider,
|
||||||
|
result[const.PROVIDER_NAME])
|
||||||
|
self.assertEqual(availability_zone_data2_json,
|
||||||
|
result[const.AVAILABILITY_ZONE_DATA])
|
||||||
|
|
||||||
|
# Check that a show reflects the new values
|
||||||
|
get_result = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.show_availability_zone_profile(
|
||||||
|
availability_zone_profile[const.ID]))
|
||||||
|
|
||||||
|
self.assertEqual(availability_zone_profile_name2,
|
||||||
|
get_result[const.NAME])
|
||||||
|
self.assertEqual(CONF.load_balancer.provider,
|
||||||
|
get_result[const.PROVIDER_NAME])
|
||||||
|
self.assertEqual(availability_zone_data2_json,
|
||||||
|
get_result[const.AVAILABILITY_ZONE_DATA])
|
||||||
|
|
||||||
|
@decorators.idempotent_id('371cee1d-3404-4744-b5c5-8a3d37aa8425')
|
||||||
|
def test_availability_zone_profile_delete(self):
|
||||||
|
"""Tests availability zone profile create and delete APIs.
|
||||||
|
|
||||||
|
* Creates an availability zone profile profile.
|
||||||
|
* Validates that other accounts cannot delete the availability zone
|
||||||
|
profile.
|
||||||
|
* Deletes the availability zone profile.
|
||||||
|
* Validates the availability zone profile is in the DELETED state.
|
||||||
|
"""
|
||||||
|
# We have to do this here as the api_version and clients are not
|
||||||
|
# setup in time to use a decorator or the skip_checks mixin
|
||||||
|
if not (self.lb_admin_availability_zone_profile_client
|
||||||
|
.is_version_supported(self.api_version, '2.14')):
|
||||||
|
raise self.skipException(
|
||||||
|
'Availability zone profiles are only available on '
|
||||||
|
'Octavia API version 2.14 or newer.')
|
||||||
|
|
||||||
|
availability_zone_profile_name = data_utils.rand_name(
|
||||||
|
"lb_admin_availabilityzoneprofile1-delete")
|
||||||
|
availability_zone_data = {
|
||||||
|
const.COMPUTE_ZONE: 'my_compute_zone',
|
||||||
|
const.MANAGEMENT_NETWORK: uuidutils.generate_uuid(),
|
||||||
|
}
|
||||||
|
availability_zone_data_json = jsonutils.dumps(availability_zone_data)
|
||||||
|
|
||||||
|
availability_zone_profile_kwargs = {
|
||||||
|
const.NAME: availability_zone_profile_name,
|
||||||
|
const.PROVIDER_NAME: CONF.load_balancer.provider,
|
||||||
|
const.AVAILABILITY_ZONE_DATA: availability_zone_data_json
|
||||||
|
}
|
||||||
|
|
||||||
|
availability_zone_profile = (
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.create_availability_zone_profile(
|
||||||
|
**availability_zone_profile_kwargs))
|
||||||
|
self.addCleanup(
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.cleanup_availability_zone_profile,
|
||||||
|
availability_zone_profile[const.ID])
|
||||||
|
|
||||||
|
# Test that a user without the load balancer admin role cannot
|
||||||
|
# delete an availability zone profile profile
|
||||||
|
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.Forbidden,
|
||||||
|
self.os_primary.availability_zone_profile_client
|
||||||
|
.delete_availability_zone_profile,
|
||||||
|
availability_zone_profile[const.ID])
|
||||||
|
|
||||||
|
# Happy path
|
||||||
|
(self.lb_admin_availability_zone_profile_client
|
||||||
|
.delete_availability_zone_profile(
|
||||||
|
availability_zone_profile[const.ID]))
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.NotFound,
|
||||||
|
self.lb_admin_availability_zone_profile_client
|
||||||
|
.show_availability_zone_profile,
|
||||||
|
availability_zone_profile[const.ID])
|
@ -52,7 +52,7 @@ class FlavorCapabilitiesAPITest(test_base.LoadBalancerBaseTest):
|
|||||||
CONF.load_balancer.provider)
|
CONF.load_balancer.provider)
|
||||||
|
|
||||||
# Check for an expected flavor capability for the configured provider
|
# Check for an expected flavor capability for the configured provider
|
||||||
admin_capabilities_client = self.lb_admin_capabilities_client
|
admin_capabilities_client = self.lb_admin_flavor_capabilities_client
|
||||||
capabilities = admin_capabilities_client.list_flavor_capabilities(
|
capabilities = admin_capabilities_client.list_flavor_capabilities(
|
||||||
CONF.load_balancer.provider)
|
CONF.load_balancer.provider)
|
||||||
|
|
||||||
|
@ -128,8 +128,16 @@ class LoadBalancerBaseTest(test.BaseTestCase):
|
|||||||
cls.mem_flavor_client = cls.os_roles_lb_member.flavor_client
|
cls.mem_flavor_client = cls.os_roles_lb_member.flavor_client
|
||||||
cls.mem_provider_client = cls.os_roles_lb_member.provider_client
|
cls.mem_provider_client = cls.os_roles_lb_member.provider_client
|
||||||
cls.os_admin_servers_client = cls.os_admin.servers_client
|
cls.os_admin_servers_client = cls.os_admin.servers_client
|
||||||
cls.lb_admin_capabilities_client = (
|
cls.lb_admin_flavor_capabilities_client = (
|
||||||
cls.os_roles_lb_admin.flavor_capabilities_client)
|
cls.os_roles_lb_admin.flavor_capabilities_client)
|
||||||
|
cls.lb_admin_availability_zone_capabilities_client = (
|
||||||
|
cls.os_roles_lb_admin.availability_zone_capabilities_client)
|
||||||
|
cls.lb_admin_availability_zone_profile_client = (
|
||||||
|
cls.os_roles_lb_admin.availability_zone_profile_client)
|
||||||
|
cls.lb_admin_availability_zone_client = (
|
||||||
|
cls.os_roles_lb_admin.availability_zone_client)
|
||||||
|
cls.mem_availability_zone_client = (
|
||||||
|
cls.os_roles_lb_member.availability_zone_client)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def resource_setup(cls):
|
def resource_setup(cls):
|
||||||
|
Loading…
Reference in New Issue
Block a user