Create api+scenario tests for listeners

This patch implements listener tests for the Octavia
Tempest Plugin.

Co-Authored-By: Adam Harwell <flux.adam@gmail.com>
Change-Id: I5c0d3a737ff4cc929573c6fb7fbb5d46f1159d80
Story: 2001387
Task: 5965
This commit is contained in:
Jude Cross 2017-08-09 15:21:04 -07:00 committed by Adam Harwell
parent a795ae6ffc
commit fbbd2b4912
12 changed files with 1735 additions and 326 deletions

View File

@ -15,6 +15,8 @@
from tempest import clients
from tempest import config
from octavia_tempest_plugin.services.load_balancer.v2 import (
listener_client)
from octavia_tempest_plugin.services.load_balancer.v2 import (
loadbalancer_client)
@ -29,3 +31,5 @@ class ManagerV2(clients.Manager):
self.loadbalancer_client = loadbalancer_client.LoadbalancerClient(
self.auth_provider, SERVICE_TYPE, CONF.identity.region)
self.listener_client = listener_client.ListenerClient(
self.auth_provider, SERVICE_TYPE, CONF.identity.region)

View File

@ -37,6 +37,21 @@ VIP_NETWORK_ID = 'vip_network_id'
VIP_PORT_ID = 'vip_port_id'
VIP_SUBNET_ID = 'vip_subnet_id'
VIP_QOS_POLICY_ID = 'vip_qos_policy_id'
PROTOCOL = 'protocol'
PROTOCOL_PORT = 'protocol_port'
LOADBALANCER_ID = 'loadbalancer_id'
CONNECTION_LIMIT = 'connection_limit'
INSERT_HEADERS = 'insert_headers'
X_FORWARDED_FOR = 'X-Forwarded-For'
X_FORWARDED_PORT = 'X-Forwarded-Port'
TIMEOUT_CLIENT_DATA = 'timeout_client_data'
TIMEOUT_MEMBER_CONNECT = 'timeout_member_connect'
TIMEOUT_MEMBER_DATA = 'timeout_member_data'
TIMEOUT_TCP_INSPECT = 'timeout_tcp_inspect'
DEFAULT_TLS_CONTAINER_REF = 'default_tls_container_ref'
SNI_CONTAINER_REFS = 'sni_container_refs'
DEFAULT_POOL_ID = 'default_pool_id'
L7_POLICIES = 'l7_policies'
# API valid fields
SHOW_LOAD_BALANCER_RESPONSE_FIELDS = (
@ -45,6 +60,15 @@ SHOW_LOAD_BALANCER_RESPONSE_FIELDS = (
UPDATED_AT, VIP_ADDRESS, VIP_NETWORK_ID, VIP_PORT_ID, VIP_SUBNET_ID,
VIP_QOS_POLICY_ID)
SHOW_LISTENER_RESPONSE_FIELDS = (
ID, NAME, DESCRIPTION, PROVISIONING_STATUS, OPERATING_STATUS,
ADMIN_STATE_UP, PROTOCOL, PROTOCOL_PORT, CONNECTION_LIMIT,
DEFAULT_TLS_CONTAINER_REF, SNI_CONTAINER_REFS, PROJECT_ID,
DEFAULT_POOL_ID, L7_POLICIES, INSERT_HEADERS, CREATED_AT, UPDATED_AT,
TIMEOUT_CLIENT_DATA, TIMEOUT_MEMBER_CONNECT, TIMEOUT_MEMBER_DATA,
TIMEOUT_TCP_INSPECT
)
# Other constants
ACTIVE = 'ACTIVE'
ADMIN_STATE_UP_TRUE = 'true'
@ -56,6 +80,11 @@ OFFLINE = 'OFFLINE'
ONLINE = 'ONLINE'
SORT = 'sort'
# Protocols
HTTP = 'HTTP'
HTTPS = 'HTTPS'
TCP = 'TCP'
# RBAC options
ADVANCED = 'advanced'
OWNERADMIN = 'owner_or_admin'

View File

@ -51,7 +51,7 @@ OctaviaGroup = [
help='Time in seconds between build status checks for '
'non-load-balancer resources to build'),
cfg.IntOpt('build_timeout',
default=30,
default=60,
help='Timeout in seconds to wait for non-load-balancer '
'resources to build'),
# load-balancer specific options

View File

@ -0,0 +1,365 @@
# Copyright 2018 GoDaddy
#
# 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 json
from oslo_log import log as logging
from tempest import config
from tempest.lib.common import rest_client
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions
from octavia_tempest_plugin.common import constants as const
from octavia_tempest_plugin.tests import waiters
CONF = config.CONF
LOG = logging.getLogger(__name__)
class Unset(object):
def __bool__(self):
return False
__nonzero__ = __bool__
def __repr__(self):
return 'Unset'
class BaseLBaaSClient(rest_client.RestClient):
root_tag = None
list_root_tag = None
base_uri = '/v2.0/lbaas/{object}'
def __init__(self, auth_provider, service, region, **kwargs):
super(BaseLBaaSClient, self).__init__(auth_provider, service,
region, **kwargs)
self.timeout = CONF.load_balancer.build_timeout
self.build_interval = CONF.load_balancer.build_interval
self.uri = self.base_uri.format(object=self.list_root_tag)
# Create a method for each object's cleanup
# This method should be used (rather than delete) for tempest cleanups.
setattr(self, 'cleanup_{}'.format(self.root_tag), self._cleanup_obj)
def _create_object(self, return_object_only=True, **kwargs):
"""Create an object.
:param return_object_only: If True, the response returns the object
inside the root tag. False returns the full
response from the API.
:param **kwargs: All attributes of the object should be passed as
keyword arguments to this function.
: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 appropriate object.
"""
obj_dict = {self.root_tag: kwargs}
response, body = self.post(self.uri, json.dumps(obj_dict))
self.expected_success(201, response.status)
if return_object_only:
return json.loads(body.decode('utf-8'))[self.root_tag]
else:
return json.loads(body.decode('utf-8'))
def _show_object(self, obj_id, query_params=None, return_object_only=True):
"""Get object details.
:param obj_id: The object 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 appropriate object.
"""
if query_params:
request_uri = '{0}/{1}?{2}'.format(self.uri, obj_id, query_params)
else:
request_uri = '{0}/{1}'.format(self.uri, obj_id)
response, body = self.get(request_uri)
self.expected_success(200, response.status)
if return_object_only:
return json.loads(body.decode('utf-8'))[self.root_tag]
else:
return json.loads(body.decode('utf-8'))
def _list_objects(self, query_params=None, return_object_only=True):
"""Get a list of the appropriate 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 appropriate objects.
"""
if query_params:
request_uri = '{0}?{1}'.format(self.uri, query_params)
else:
request_uri = self.uri
response, body = self.get(request_uri)
self.expected_success(200, response.status)
if return_object_only:
return json.loads(body.decode('utf-8'))[self.list_root_tag]
else:
return json.loads(body.decode('utf-8'))
def _update_object(self, obj_id, return_object_only=True, **kwargs):
"""Update an object.
:param obj_id: The object ID to update.
:param return_object_only: If True, the response returns the object
inside the root tag. False returns the full
response from the API.
:param **kwargs: All attributes of the object should be passed as
keyword arguments to this function.
: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 appropriate object.
"""
obj_dict = {self.root_tag: kwargs}
uri = '{0}/{1}'.format(self.uri, obj_id)
response, body = self.put(uri, json.dumps(obj_dict))
self.expected_success(200, response.status)
if return_object_only:
return json.loads(body.decode('utf-8'))[self.root_tag]
else:
return json.loads(body.decode('utf-8'))
def _delete_obj(self, obj_id, ignore_errors=False, cascade=False):
"""Delete an object.
:param obj_id: The object ID to delete.
:param ignore_errors: True if errors should be ignored.
:param cascade: If true will delete all child objects of an
object, if that object supports it.
: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.
"""
if cascade:
uri = '{0}/{1}?cascade=true'.format(self.uri, obj_id)
else:
uri = '{0}/{1}'.format(self.uri, obj_id)
if ignore_errors:
try:
response, body = self.delete(uri)
except ignore_errors:
return
else:
response, body = self.delete(uri)
self.expected_success(204, response.status)
return response.status
def _cleanup_obj(self, obj_id, lb_client=None, lb_id=None):
"""Clean up an object (for use in tempest addClassResourceCleanup).
We always need to wait for the parent LB to be in a mutable state
before deleting the child object, and the cleanups will not guarantee
this if we just pass the delete function to tempest cleanup.
For example, if we add multiple listeners on the same LB to cleanup,
tempest will delete the first one and then immediately try to delete
the second one, which will fail because the LB will be immutable.
This function:
* Waits until the parent LB is ACTIVE
* Deletes the object
:param obj_id: The object ID to clean up.
:param lb_client: (Optional) The loadbalancer client, if this isn't the
loadbalancer client already.
:param lb_id: (Optional) The ID of the parent loadbalancer, if the main
obj_id is for a sub-object and not a loadbalancer.
:return:
"""
if lb_client and lb_id:
wait_id = lb_id
wait_client = lb_client
wait_func = lb_client.show_loadbalancer
else:
wait_id = obj_id
wait_client = self
wait_func = self._show_object
LOG.info("Starting cleanup for %s %s...", self.root_tag, obj_id)
LOG.info("Waiting for %s %s to be ACTIVE...",
wait_client.root_tag, wait_id)
try:
waiters.wait_for_status(wait_func, wait_id,
const.PROVISIONING_STATUS,
const.ACTIVE,
self.build_interval,
self.timeout)
except exceptions.UnexpectedResponseCode:
# Status is ERROR, go ahead with deletion
LOG.debug("Found %s %s in ERROR status, proceeding with cleanup.",
wait_client.root_tag, wait_id)
except exceptions.TimeoutException:
# Timed out, nothing to be done, let errors happen
LOG.error("Timeout exceeded waiting to clean up %s %s.",
self.root_tag, obj_id)
except exceptions.NotFound:
# Already gone, cleanup complete
LOG.info("%s %s is already gone. Cleanup considered complete.",
wait_client.root_tag.capitalize(), wait_id)
return
except Exception as e:
# Log that something weird happens, then let the chips fall
LOG.error("Cleanup encountered an unknown exception while waiting "
"for %s %s: %s", wait_client.root_tag, wait_id, e)
uri = '{0}/{1}'.format(self.uri, obj_id)
LOG.info("Cleaning up %s %s...", self.root_tag, obj_id)
return_status = test_utils.call_and_ignore_notfound_exc(
self.delete, uri)
LOG.info("Cleanup complete for %s %s...", self.root_tag, obj_id)
return return_status
def is_resource_deleted(self, id):
"""Check if the object is deleted.
:param id: The object ID to check.
:return: boolean state representing the object's deleted state
"""
try:
obj = self._show_object(id)
if obj.get(const.PROVISIONING_STATUS) == const.DELETED:
return True
except exceptions.NotFound:
return True
return False

View File

@ -0,0 +1,293 @@
# Copyright 2017 GoDaddy
#
# 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 octavia_tempest_plugin.services.load_balancer.v2 import base_client
CONF = config.CONF
Unset = base_client.Unset
class ListenerClient(base_client.BaseLBaaSClient):
root_tag = 'listener'
list_root_tag = 'listeners'
def create_listener(self, protocol, protocol_port, loadbalancer_id,
name=Unset, description=Unset, admin_state_up=Unset,
connection_limit=Unset, timeout_client_data=Unset,
timeout_member_connect=Unset,
timeout_member_data=Unset, timeout_tcp_inspect=Unset,
insert_headers=Unset, default_pool_id=Unset,
default_tls_container_ref=Unset,
sni_container_refs=Unset, return_object_only=True):
"""Create a listener.
:param protocol: The protocol for the resource.
:param protocol_port: The protocol port number for the resource.
:param loadbalancer_id: The ID of the load balancer.
:param name: Human-readable name of the resource.
:param description: A human-readable description for the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param connection_limit: The maximum number of connections permitted
for this listener. Default value is -1 which
represents infinite connections.
:param timeout_client_data: Frontend client inactivity timeout in
milliseconds.
:param timeout_member_connect: Backend member connection timeout in
milliseconds.
:param timeout_member_data: Backend member inactivity timeout in
milliseconds.
:param timeout_tcp_inspect: Time, in milliseconds, to wait for
additional TCP packets for content
inspection.
:param insert_headers: A dictionary of optional headers to insert into
the request before it is sent to the backend
member.
:param default_pool_id: The ID of the pool used by the listener if no
L7 policies match.
:param default_tls_container_ref: The URI of the key manager service
secret containing a PKCS12 format
certificate/key bundle for
TERMINATED_TLS listeners.
:param sni_container_refs: A list of URIs to the key manager service
secrets containing PKCS12 format
certificate/key bundles for TERMINATED_TLS
listeners.
: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 listener 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_listener(self, listener_id, query_params=None,
return_object_only=True):
"""Get listener details.
:param listener_id: The listener 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: A listener object.
"""
return self._show_object(obj_id=listener_id,
query_params=query_params,
return_object_only=return_object_only)
def list_listeners(self, query_params=None, return_object_only=True):
"""Get a list of listener 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 listener objects.
"""
return self._list_objects(query_params=query_params,
return_object_only=return_object_only)
def update_listener(self, listener_id, name=Unset, description=Unset,
admin_state_up=Unset, connection_limit=Unset,
timeout_client_data=Unset,
timeout_member_connect=Unset,
timeout_member_data=Unset, timeout_tcp_inspect=Unset,
insert_headers=Unset, default_pool_id=Unset,
default_tls_container_ref=Unset,
sni_container_refs=Unset, return_object_only=True):
"""Update a listener.
:param listener_id: The listener ID to update.
:param name: Human-readable name of the resource.
:param description: A human-readable description for the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param connection_limit: The maximum number of connections permitted
for this listener. Default value is -1 which
represents infinite connections.
:param timeout_client_data: Frontend client inactivity timeout in
milliseconds.
:param timeout_member_connect: Backend member connection timeout in
milliseconds.
:param timeout_member_data: Backend member inactivity timeout in
milliseconds.
:param timeout_tcp_inspect: Time, in milliseconds, to wait for
additional TCP packets for content
inspection.
:param insert_headers: A dictionary of optional headers to insert into
the request before it is sent to the backend
member.
:param default_pool_id: The ID of the pool used by the listener if no
L7 policies match.
:param default_tls_container_ref: The URI of the key manager service
secret containing a PKCS12 format
certificate/key bundle for
TERMINATED_TLS listeners.
:param sni_container_refs: A list of URIs to the key manager service
secrets containing PKCS12 format
certificate/key bundles for TERMINATED_TLS
listeners.
: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 listener object.
"""
kwargs = {arg: value for arg, value in locals().items()
if arg != 'self' and value is not Unset}
kwargs['obj_id'] = kwargs.pop('listener_id')
return self._update_object(**kwargs)
def delete_listener(self, listener_id, ignore_errors=False):
"""Delete an object.
:param listener_id: The listener 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=listener_id,
ignore_errors=ignore_errors)

View File

@ -16,132 +16,38 @@
import json
from tempest import config
from tempest.lib.common import rest_client
from octavia_tempest_plugin.services.load_balancer.v2 import base_client
CONF = config.CONF
Unset = base_client.Unset
class LoadbalancerClient(rest_client.RestClient):
class LoadbalancerClient(base_client.BaseLBaaSClient):
_uri = '/v2.0/lbaas/loadbalancers'
root_tag = 'loadbalancer'
list_root_tag = 'loadbalancers'
def __init__(self, auth_provider, service, region, **kwargs):
super(LoadbalancerClient, self).__init__(auth_provider, service,
region, **kwargs)
self.timeout = CONF.load_balancer.lb_build_timeout
self.build_interval = CONF.load_balancer.lb_build_interval
self.resource_name = 'load balancer'
self.get_status = self.show_loadbalancer
def list_loadbalancers(self, query_params=None, return_object_only=True):
"""Get a list of load balancers.
: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 load balancers object.
"""
if query_params:
request_uri = '{0}?{1}'.format(self._uri, query_params)
else:
request_uri = self._uri
response, body = self.get(request_uri)
self.expected_success(200, response.status)
if return_object_only:
return json.loads(body.decode('utf-8'))['loadbalancers']
else:
return json.loads(body.decode('utf-8'))
def create_loadbalancer_dict(self, lb_dict, return_object_only=True):
"""Create a load balancer using a dictionary.
Example lb_dict::
lb_dict = {'loadbalancer': {
'vip_network_id': 'd0be73da-921a-4e03-9c49-f13f18f7e39f',
'name': 'TEMPEST_TEST_LB',
'description': 'LB for Tempest tests'}
}
:param lb_dict: A dictionary describing the load balancer.
: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 load balancer object.
"""
response, body = self.post(self._uri, json.dumps(lb_dict))
self.expected_success(201, response.status)
if return_object_only:
return json.loads(body.decode('utf-8'))['loadbalancer']
else:
return json.loads(body.decode('utf-8'))
def create_loadbalancer(self, admin_state_up=None, description=None,
flavor=None, listeners=None, name=None,
project_id=None, provider=None, vip_address=None,
vip_network_id=None, vip_port_id=None,
vip_qos_policy_id=None, vip_subnet_id=None,
return_object_only=True):
"""Create a load balancer.
def create_loadbalancer(self, name=Unset, description=Unset,
admin_state_up=Unset, flavor_id=Unset,
listeners=Unset, project_id=Unset, provider=Unset,
vip_address=Unset, vip_network_id=Unset,
vip_port_id=Unset, vip_qos_policy_id=Unset,
vip_subnet_id=Unset, return_object_only=True):
"""Create a loadbalancer.
:param name: Human-readable name of the resource.
:param description: A human-readable description for the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param description: A human-readable description for the resource.
:param flavor: The load balancer flavor ID.
:param listeners: A list of listner dictionaries.
:param name: Human-readable name of the resource.
:param project_id: The ID of the project owning this resource.
:param provider: Provider name for the load balancer.
:param vip_address: The IP address of the Virtual IP (VIP).
@ -178,24 +84,145 @@ class LoadbalancerClient(rest_client.RestClient):
of the handled checks
:raises UnprocessableEntity: If a 422 response code is received and
couldn't be parsed
:returns: A load balancer object.
:returns: A loadbalancer object.
"""
method_args = locals()
lb_params = {}
for param, value in method_args.items():
if param not in ('self',
'return_object_only') and value is not None:
lb_params[param] = value
lb_dict = {'loadbalancer': lb_params}
return self.create_loadbalancer_dict(lb_dict, return_object_only)
kwargs = {arg: value for arg, value in locals().items()
if arg != 'self' and value is not Unset}
return self._create_object(**kwargs)
def show_loadbalancer(self, lb_id, query_params=None,
return_object_only=True):
"""Get loadbalancer details.
:param lb_id: The loadbalancer 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: A loadbalancer object.
"""
return self._show_object(obj_id=lb_id,
query_params=query_params,
return_object_only=return_object_only)
def list_loadbalancers(self, query_params=None, return_object_only=True):
"""Get a list of loadbalancer 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 loadbalancer objects.
"""
return self._list_objects(query_params=query_params,
return_object_only=return_object_only)
def update_loadbalancer(self, lb_id, name=Unset, description=Unset,
admin_state_up=Unset, vip_qos_policy_id=Unset,
return_object_only=True):
"""Update a loadbalancer.
:param lb_id: The loadbalancer ID to update.
:param name: Human-readable name of the resource.
:param description: A human-readable description for the resource.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param vip_qos_policy_id: The ID of the QoS Policy which will apply to
the Virtual IP (VIP).
: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 loadbalancer object.
"""
kwargs = {arg: value for arg, value in locals().items()
if arg != 'self' and value is not Unset}
kwargs['obj_id'] = kwargs.pop('lb_id')
return self._update_object(**kwargs)
def delete_loadbalancer(self, lb_id, cascade=False, ignore_errors=False):
"""Delete a load balancer.
"""Delete an object.
:param lb_id: The load balancer ID to delete.
:param cascade: If true will delete all child objects of the
load balancer.
:param lb_id: The loadbalancer ID to delete.
:param ignore_errors: True if errors should be ignored.
:param cascade: If true will delete all child objects of an
object, if that object supports it.
:raises AssertionError: if the expected_code isn't a valid http success
response code
:raises BadRequest: If a 400 response code is received
@ -224,20 +251,9 @@ class LoadbalancerClient(rest_client.RestClient):
:returns: None if ignore_errors is True, the response status code
if not.
"""
if cascade:
uri = '{0}/{1}?cascade=true'.format(self._uri, lb_id)
else:
uri = '{0}/{1}'.format(self._uri, lb_id)
if ignore_errors:
try:
response, body = self.delete(uri)
except ignore_errors:
return
else:
response, body = self.delete(uri)
self.expected_success(204, response.status)
return response.status
return self._delete_obj(obj_id=lb_id,
ignore_errors=ignore_errors,
cascade=cascade)
def failover_loadbalancer(self, lb_id):
"""Failover a load balancer.
@ -270,60 +286,11 @@ class LoadbalancerClient(rest_client.RestClient):
couldn't be parsed
:returns: None
"""
uri = '{0}/{1}/failover'.format(self._uri, lb_id)
uri = '{0}/{1}/failover'.format(self.uri, lb_id)
response, body = self.put(uri, '')
self.expected_success(202, response.status)
return
def show_loadbalancer(self, lb_id, query_params=None,
return_object_only=True):
"""Get load balancer details.
:param lb_id: The load balancer 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: A load balancer object.
"""
if query_params:
request_uri = '{0}/{1}?{2}'.format(self._uri, lb_id, query_params)
else:
request_uri = '{0}/{1}'.format(self._uri, lb_id)
response, body = self.get(request_uri)
self.expected_success(200, response.status)
if return_object_only:
return json.loads(body.decode('utf-8'))['loadbalancer']
else:
return json.loads(body.decode('utf-8'))
def get_loadbalancer_stats(self, lb_id, query_params=None,
return_object_only=True):
"""Get load balancer statistics.
@ -362,10 +329,10 @@ class LoadbalancerClient(rest_client.RestClient):
:returns: A load balancer statistics object.
"""
if query_params:
request_uri = '{0}/{1}/stats?{2}'.format(self._uri, lb_id,
request_uri = '{0}/{1}/stats?{2}'.format(self.uri, lb_id,
query_params)
else:
request_uri = '{0}/{1}/stats'.format(self._uri, lb_id)
request_uri = '{0}/{1}/stats'.format(self.uri, lb_id)
response, body = self.get(request_uri)
self.expected_success(200, response.status)
@ -412,10 +379,10 @@ class LoadbalancerClient(rest_client.RestClient):
:returns: A load balancer statuses object.
"""
if query_params:
request_uri = '{0}/{1}/status?{2}'.format(self._uri, lb_id,
request_uri = '{0}/{1}/status?{2}'.format(self.uri, lb_id,
query_params)
else:
request_uri = '{0}/{1}/status'.format(self._uri, lb_id)
request_uri = '{0}/{1}/status'.format(self.uri, lb_id)
response, body = self.get(request_uri)
self.expected_success(200, response.status)
@ -423,104 +390,3 @@ class LoadbalancerClient(rest_client.RestClient):
return json.loads(body.decode('utf-8'))['statuses']
else:
return json.loads(body.decode('utf-8'))
def update_loadbalancer_dict(self, lb_id, lb_dict,
return_object_only=True):
"""Update a load balancer using a dictionary.
Example lb_dict::
lb_dict = {'loadbalancer': {'name': 'TEMPEST_TEST_LB_UPDATED'} }
:param lb_id: The load balancer ID to update.
:param lb_dict: A dictionary of elements to update on the load
balancer.
: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 load balancer object.
"""
uri = '{0}/{1}'.format(self._uri, lb_id)
response, body = self.put(uri, json.dumps(lb_dict))
self.expected_success(200, response.status)
if return_object_only:
return json.loads(body.decode('utf-8'))['loadbalancer']
else:
return json.loads(body.decode('utf-8'))
def update_loadbalancer(self, lb_id, admin_state_up=None, description=None,
name=None, vip_qos_policy_id=None,
return_object_only=True):
"""Update a load balancer.
:param lb_id: The load balancer ID to update.
:param admin_state_up: The administrative state of the resource, which
is up (true) or down (false).
:param description: A human-readable description for the resource.
:param name: Human-readable name of the resource.
:param vip_qos_policy_id: The ID of the QoS Policy which will apply to
the Virtual IP (VIP).
: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 load balancer object.
"""
method_args = locals()
lb_params = {}
for param, value in method_args.items():
if param not in ('self', 'lb_id',
'return_object_only') and value is not None:
lb_params[param] = value
lb_dict = {'loadbalancer': lb_params}
return self.update_loadbalancer_dict(lb_id, lb_dict,
return_object_only)

View File

@ -0,0 +1,679 @@
# Copyright 2018 GoDaddy
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import time
from uuid import UUID
from dateutil import parser
from oslo_utils import strutils
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
from octavia_tempest_plugin.tests import waiters
CONF = config.CONF
class ListenerAPITest(test_base.LoadBalancerBaseTest):
"""Test the listener object API."""
@classmethod
def resource_setup(cls):
"""Setup resources needed by the tests."""
super(ListenerAPITest, cls).resource_setup()
lb_name = data_utils.rand_name("lb_member_lb1_listener")
lb_kwargs = {const.ADMIN_STATE_UP: False,
const.PROVIDER: CONF.load_balancer.provider,
const.NAME: lb_name}
ip_version = 6 if CONF.load_balancer.test_with_ipv6 else 4
cls._setup_lb_network_kwargs(lb_kwargs, ip_version)
lb = cls.mem_lb_client.create_loadbalancer(**lb_kwargs)
cls.lb_id = lb[const.ID]
cls.addClassResourceCleanup(
cls.mem_lb_client.cleanup_loadbalancer,
cls.lb_id)
waiters.wait_for_status(cls.mem_lb_client.show_loadbalancer,
cls.lb_id, const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.lb_build_interval,
CONF.load_balancer.lb_build_timeout)
@decorators.idempotent_id('88d0ec83-7b08-48d9-96e2-0df1d2f8cd98')
def test_listener_create(self):
"""Tests listener create and basic show APIs.
* Tests that users without the listener member role cannot
* create listeners.
* Create a fully populated listener.
* Show listener details.
* Validate the show reflects the requested values.
"""
listener_name = data_utils.rand_name("lb_member_listener1-create")
listener_description = data_utils.arbitrary_string(size=255)
listener_kwargs = {
const.NAME: listener_name,
const.DESCRIPTION: listener_description,
const.ADMIN_STATE_UP: True,
const.PROTOCOL: const.HTTP,
const.PROTOCOL_PORT: 80,
const.LOADBALANCER_ID: self.lb_id,
const.CONNECTION_LIMIT: 200,
const.TIMEOUT_CLIENT_DATA: 1000,
const.TIMEOUT_MEMBER_CONNECT: 1000,
const.TIMEOUT_MEMBER_DATA: 1000,
const.TIMEOUT_TCP_INSPECT: 50,
const.INSERT_HEADERS: {
const.X_FORWARDED_FOR: "true",
const.X_FORWARDED_PORT: "true"
},
# TODO(rm_work): need to finish the rest of this stuff
# const.DEFAULT_POOL_ID: '',
# const.DEFAULT_TLS_CONTAINER_REF: '',
# const.SNI_CONTAINER_REFS: [],
}
# Test that a user without the load balancer role cannot
# create a listener
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
self.assertRaises(
exceptions.Forbidden,
self.os_primary.listener_client.create_listener,
**listener_kwargs)
listener = self.mem_listener_client.create_listener(**listener_kwargs)
self.addClassResourceCleanup(
self.mem_listener_client.cleanup_listener,
listener[const.ID],
lb_client=self.mem_lb_client, lb_id=self.lb_id)
listener = waiters.wait_for_status(
self.mem_listener_client.show_listener,
listener[const.ID], const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
self.assertEqual(listener_name, listener[const.NAME])
self.assertEqual(listener_description, listener[const.DESCRIPTION])
self.assertTrue(listener[const.ADMIN_STATE_UP])
parser.parse(listener[const.CREATED_AT])
parser.parse(listener[const.UPDATED_AT])
UUID(listener[const.ID])
# Operating status is a measured status, so no-op will not go online
if CONF.load_balancer.test_with_noop:
self.assertEqual(const.OFFLINE, listener[const.OPERATING_STATUS])
else:
self.assertEqual(const.ONLINE, listener[const.OPERATING_STATUS])
self.assertEqual(const.HTTP, listener[const.PROTOCOL])
self.assertEqual(80, listener[const.PROTOCOL_PORT])
self.assertEqual(200, listener[const.CONNECTION_LIMIT])
insert_headers = listener[const.INSERT_HEADERS]
self.assertTrue(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_FOR]))
self.assertTrue(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_PORT]))
self.assertEqual(1000, listener[const.TIMEOUT_CLIENT_DATA])
self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_CONNECT])
self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_DATA])
self.assertEqual(50, listener[const.TIMEOUT_TCP_INSPECT])
@decorators.idempotent_id('78ba6eb0-178c-477e-9156-b6775ca7b271')
def test_listener_list(self):
"""Tests listener list API and field filtering.
* Create a clean loadbalancer.
* Create three listeners.
* Validates that other accounts cannot list the listeners.
* List the listeners using the default sort order.
* List the listeners using descending sort order.
* List the listeners using ascending sort order.
* List the listeners returning one field at a time.
* List the listeners returning two fields.
* List the listeners filtering to one of the three.
* List the listeners filtered, one field, and sorted.
"""
lb_name = data_utils.rand_name("lb_member_lb2_listener-list")
lb = self.mem_lb_client.create_loadbalancer(
name=lb_name,
vip_network_id=self.lb_member_vip_net[const.ID])
lb_id = lb[const.ID]
self.addClassResourceCleanup(
self.mem_lb_client.cleanup_loadbalancer,
lb_id)
waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
lb_id,
const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.lb_build_interval,
CONF.load_balancer.lb_build_timeout)
listener1_name = data_utils.rand_name("lb_member_listener2-list")
listener1_desc = 'B'
listener1_kwargs = {
const.NAME: listener1_name,
const.DESCRIPTION: listener1_desc,
const.ADMIN_STATE_UP: True,
const.PROTOCOL: const.HTTP,
const.PROTOCOL_PORT: 80,
const.LOADBALANCER_ID: lb_id,
}
listener1 = self.mem_listener_client.create_listener(
**listener1_kwargs)
self.addClassResourceCleanup(
self.mem_listener_client.cleanup_listener,
listener1[const.ID],
lb_client=self.mem_lb_client, lb_id=lb_id)
listener1 = waiters.wait_for_status(
self.mem_listener_client.show_listener, listener1[const.ID],
const.PROVISIONING_STATUS, const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
lb_id,
const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
# Time resolution for created_at is only to the second, and we need to
# ensure that each object has a distinct creation time. Delaying one
# second is both a simple and a reliable way to accomplish this.
time.sleep(1)
listener2_name = data_utils.rand_name("lb_member_listener1-list")
listener2_desc = 'A'
listener2_kwargs = {
const.NAME: listener2_name,
const.DESCRIPTION: listener2_desc,
const.ADMIN_STATE_UP: True,
const.PROTOCOL: const.HTTP,
const.PROTOCOL_PORT: 81,
const.LOADBALANCER_ID: lb_id,
}
listener2 = self.mem_listener_client.create_listener(
**listener2_kwargs)
self.addClassResourceCleanup(
self.mem_listener_client.cleanup_listener,
listener2[const.ID],
lb_client=self.mem_lb_client, lb_id=lb_id)
listener2 = waiters.wait_for_status(
self.mem_listener_client.show_listener, listener2[const.ID],
const.PROVISIONING_STATUS, const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
lb_id,
const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
# Time resolution for created_at is only to the second, and we need to
# ensure that each object has a distinct creation time. Delaying one
# second is both a simple and a reliable way to accomplish this.
time.sleep(1)
listener3_name = data_utils.rand_name("lb_member_listener3-list")
listener3_desc = 'C'
listener3_kwargs = {
const.NAME: listener3_name,
const.DESCRIPTION: listener3_desc,
const.ADMIN_STATE_UP: False,
const.PROTOCOL: const.HTTP,
const.PROTOCOL_PORT: 82,
const.LOADBALANCER_ID: lb_id,
}
listener3 = self.mem_listener_client.create_listener(
**listener3_kwargs)
self.addClassResourceCleanup(
self.mem_listener_client.cleanup_listener,
listener3[const.ID],
lb_client=self.mem_lb_client, lb_id=lb_id)
listener3 = waiters.wait_for_status(
self.mem_listener_client.show_listener, listener3[const.ID],
const.PROVISIONING_STATUS, const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
lb_id,
const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
# Test that a different user cannot list listeners
if not CONF.load_balancer.RBAC_test_type == const.NONE:
member2_client = self.os_roles_lb_member2.listener_client
primary = member2_client.list_listeners(
query_params='loadbalancer_id={lb_id}'.format(lb_id=lb_id))
self.assertEqual(0, len(primary))
# Test that a user without the lb member role cannot list load
# balancers
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
self.assertRaises(
exceptions.Forbidden,
self.os_primary.listener_client.list_listeners)
# Check the default sort order, created_at
listeners = self.mem_listener_client.list_listeners(
query_params='loadbalancer_id={lb_id}'.format(lb_id=lb_id))
self.assertEqual(listener1[const.DESCRIPTION],
listeners[0][const.DESCRIPTION])
self.assertEqual(listener2[const.DESCRIPTION],
listeners[1][const.DESCRIPTION])
self.assertEqual(listener3[const.DESCRIPTION],
listeners[2][const.DESCRIPTION])
# Test sort descending by description
listeners = self.mem_listener_client.list_listeners(
query_params='loadbalancer_id={lb_id}&{sort}={descr}:{desc}'
.format(lb_id=lb_id, sort=const.SORT,
descr=const.DESCRIPTION, desc=const.DESC))
self.assertEqual(listener1[const.DESCRIPTION],
listeners[1][const.DESCRIPTION])
self.assertEqual(listener2[const.DESCRIPTION],
listeners[2][const.DESCRIPTION])
self.assertEqual(listener3[const.DESCRIPTION],
listeners[0][const.DESCRIPTION])
# Test sort ascending by description
listeners = self.mem_listener_client.list_listeners(
query_params='loadbalancer_id={lb_id}&{sort}={descr}:{asc}'
.format(lb_id=lb_id, sort=const.SORT,
descr=const.DESCRIPTION, asc=const.ASC))
self.assertEqual(listener1[const.DESCRIPTION],
listeners[1][const.DESCRIPTION])
self.assertEqual(listener2[const.DESCRIPTION],
listeners[0][const.DESCRIPTION])
self.assertEqual(listener3[const.DESCRIPTION],
listeners[2][const.DESCRIPTION])
# Test fields
for field in const.SHOW_LISTENER_RESPONSE_FIELDS:
if field in (const.DEFAULT_POOL_ID, const.L7_POLICIES):
continue
listeners = self.mem_listener_client.list_listeners(
query_params='loadbalancer_id={lb_id}&{fields}={field}'
.format(lb_id=lb_id,
fields=const.FIELDS, field=field))
self.assertEqual(1, len(listeners[0]))
self.assertEqual(listener1[field], listeners[0][field])
self.assertEqual(listener2[field], listeners[1][field])
self.assertEqual(listener3[field], listeners[2][field])
# Test multiple fields at the same time
listeners = self.mem_listener_client.list_listeners(
query_params='loadbalancer_id={lb_id}&{fields}={admin}&'
'{fields}={created}'.format(
lb_id=lb_id, fields=const.FIELDS,
admin=const.ADMIN_STATE_UP,
created=const.CREATED_AT))
self.assertEqual(2, len(listeners[0]))
self.assertTrue(listeners[0][const.ADMIN_STATE_UP])
parser.parse(listeners[0][const.CREATED_AT])
self.assertTrue(listeners[1][const.ADMIN_STATE_UP])
parser.parse(listeners[1][const.CREATED_AT])
self.assertFalse(listeners[2][const.ADMIN_STATE_UP])
parser.parse(listeners[2][const.CREATED_AT])
# Test filtering
listeners = self.mem_listener_client.list_listeners(
query_params='loadbalancer_id={lb_id}&{desc}={lb_desc}'.format(
lb_id=lb_id, desc=const.DESCRIPTION,
lb_desc=listener2[const.DESCRIPTION]))
self.assertEqual(1, len(listeners))
self.assertEqual(listener2[const.DESCRIPTION],
listeners[0][const.DESCRIPTION])
# Test combined params
listeners = self.mem_listener_client.list_listeners(
query_params='loadbalancer_id={lb_id}&{admin}={true}&'
'{fields}={descr}&{fields}={id}&'
'{sort}={descr}:{desc}'.format(
lb_id=lb_id, admin=const.ADMIN_STATE_UP,
true=const.ADMIN_STATE_UP_TRUE,
fields=const.FIELDS, descr=const.DESCRIPTION,
id=const.ID, sort=const.SORT, desc=const.DESC))
# Should get two listeners
self.assertEqual(2, len(listeners))
# listeners should have two fields
self.assertEqual(2, len(listeners[0]))
# Should be in descending order
self.assertEqual(listener2[const.DESCRIPTION],
listeners[1][const.DESCRIPTION])
self.assertEqual(listener1[const.DESCRIPTION],
listeners[0][const.DESCRIPTION])
# Attempt to clean up ahead of time
try:
self.mem_lb_client.delete_loadbalancer(lb_id, cascade=True)
waiters.wait_for_deleted_status_or_not_found(
self.mem_lb_client.show_loadbalancer, lb_id,
const.PROVISIONING_STATUS,
CONF.load_balancer.check_interval,
CONF.load_balancer.check_timeout)
except Exception:
pass
@decorators.idempotent_id('6e299eae-6907-4dfc-89c2-e57709d25d3d')
def test_listener_show(self):
"""Tests listener show API.
* Create a fully populated listener.
* Show listener details.
* Validate the show reflects the requested values.
* Validates that other accounts cannot see the listener.
"""
listener_name = data_utils.rand_name("lb_member_listener1-show")
listener_description = data_utils.arbitrary_string(size=255)
listener_kwargs = {
const.NAME: listener_name,
const.DESCRIPTION: listener_description,
const.ADMIN_STATE_UP: True,
const.PROTOCOL: const.HTTP,
const.PROTOCOL_PORT: 81,
const.LOADBALANCER_ID: self.lb_id,
const.CONNECTION_LIMIT: 200,
const.TIMEOUT_CLIENT_DATA: 1000,
const.TIMEOUT_MEMBER_CONNECT: 1000,
const.TIMEOUT_MEMBER_DATA: 1000,
const.TIMEOUT_TCP_INSPECT: 50,
const.INSERT_HEADERS: {
const.X_FORWARDED_FOR: "true",
const.X_FORWARDED_PORT: "true"
},
# TODO(rm_work): need to finish the rest of this stuff
# const.DEFAULT_POOL_ID: '',
# const.DEFAULT_TLS_CONTAINER_REF: '',
# const.SNI_CONTAINER_REFS: [],
}
listener = self.mem_listener_client.create_listener(**listener_kwargs)
self.addClassResourceCleanup(
self.mem_listener_client.cleanup_listener,
listener[const.ID],
lb_client=self.mem_lb_client, lb_id=self.lb_id)
listener = waiters.wait_for_status(
self.mem_listener_client.show_listener,
listener[const.ID], const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
self.assertEqual(listener_name, listener[const.NAME])
self.assertEqual(listener_description, listener[const.DESCRIPTION])
self.assertTrue(listener[const.ADMIN_STATE_UP])
parser.parse(listener[const.CREATED_AT])
parser.parse(listener[const.UPDATED_AT])
UUID(listener[const.ID])
# Operating status is a measured status, so no-op will not go online
if CONF.load_balancer.test_with_noop:
self.assertEqual(const.OFFLINE, listener[const.OPERATING_STATUS])
else:
self.assertEqual(const.ONLINE, listener[const.OPERATING_STATUS])
self.assertEqual(const.HTTP, listener[const.PROTOCOL])
self.assertEqual(81, listener[const.PROTOCOL_PORT])
self.assertEqual(200, listener[const.CONNECTION_LIMIT])
insert_headers = listener[const.INSERT_HEADERS]
self.assertTrue(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_FOR]))
self.assertTrue(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_PORT]))
self.assertEqual(1000, listener[const.TIMEOUT_CLIENT_DATA])
self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_CONNECT])
self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_DATA])
self.assertEqual(50, listener[const.TIMEOUT_TCP_INSPECT])
# Test that a user with lb_admin role can see the listener
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
listener_client = self.os_roles_lb_admin.listener_client
listener_adm = listener_client.show_listener(listener[const.ID])
self.assertEqual(listener_name, listener_adm[const.NAME])
# Test that a user with cloud admin role can see the listener
if not CONF.load_balancer.RBAC_test_type == const.NONE:
adm = self.os_admin.listener_client.show_listener(
listener[const.ID])
self.assertEqual(listener_name, adm[const.NAME])
# Test that a different user, with load balancer member role, cannot
# see this listener
if not CONF.load_balancer.RBAC_test_type == const.NONE:
member2_client = self.os_roles_lb_member2.listener_client
self.assertRaises(exceptions.Forbidden,
member2_client.show_listener,
listener[const.ID])
# Test that a user, without the load balancer member role, cannot
# show listeners
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
self.assertRaises(
exceptions.Forbidden,
self.os_primary.listener_client.show_listener,
listener[const.ID])
@decorators.idempotent_id('aaae0298-5778-4c7e-a27a-01549a71b319')
def test_listener_update(self):
"""Tests listener show API and field filtering.
* Create a fully populated listener.
* Show listener details.
* Validate the show reflects the initial values.
* Validates that other accounts cannot update the listener.
* Update the listener details.
* Show listener details.
* Validate the show reflects the initial values.
"""
listener_name = data_utils.rand_name("lb_member_listener1-update")
listener_description = data_utils.arbitrary_string(size=255)
listener_kwargs = {
const.NAME: listener_name,
const.DESCRIPTION: listener_description,
const.ADMIN_STATE_UP: False,
const.PROTOCOL: const.HTTP,
const.PROTOCOL_PORT: 82,
const.LOADBALANCER_ID: self.lb_id,
const.CONNECTION_LIMIT: 200,
const.TIMEOUT_CLIENT_DATA: 1000,
const.TIMEOUT_MEMBER_CONNECT: 1000,
const.TIMEOUT_MEMBER_DATA: 1000,
const.TIMEOUT_TCP_INSPECT: 50,
const.INSERT_HEADERS: {
const.X_FORWARDED_FOR: "true",
const.X_FORWARDED_PORT: "true"
},
# TODO(rm_work): need to finish the rest of this stuff
# const.DEFAULT_POOL_ID: '',
# const.DEFAULT_TLS_CONTAINER_REF: '',
# const.SNI_CONTAINER_REFS: [],
}
listener = self.mem_listener_client.create_listener(**listener_kwargs)
self.addClassResourceCleanup(
self.mem_listener_client.cleanup_listener,
listener[const.ID],
lb_client=self.mem_lb_client, lb_id=self.lb_id)
listener = waiters.wait_for_status(
self.mem_listener_client.show_listener,
listener[const.ID], const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
self.assertEqual(listener_name, listener[const.NAME])
self.assertEqual(listener_description, listener[const.DESCRIPTION])
self.assertFalse(listener[const.ADMIN_STATE_UP])
parser.parse(listener[const.CREATED_AT])
parser.parse(listener[const.UPDATED_AT])
UUID(listener[const.ID])
# Operating status is a measured status, so no-op will not go online
if CONF.load_balancer.test_with_noop:
self.assertEqual(const.OFFLINE, listener[const.OPERATING_STATUS])
else:
self.assertEqual(const.ONLINE, listener[const.OPERATING_STATUS])
self.assertEqual(const.HTTP, listener[const.PROTOCOL])
self.assertEqual(82, listener[const.PROTOCOL_PORT])
self.assertEqual(200, listener[const.CONNECTION_LIMIT])
insert_headers = listener[const.INSERT_HEADERS]
self.assertTrue(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_FOR]))
self.assertTrue(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_PORT]))
self.assertEqual(1000, listener[const.TIMEOUT_CLIENT_DATA])
self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_CONNECT])
self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_DATA])
self.assertEqual(50, listener[const.TIMEOUT_TCP_INSPECT])
new_name = data_utils.rand_name("lb_member_listener1-update")
new_description = data_utils.arbitrary_string(size=255,
base_text='new')
# Test that a user, without the load balancer member role, cannot
# use this command
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
self.assertRaises(
exceptions.Forbidden,
self.os_primary.listener_client.update_listener,
listener[const.ID], admin_state_up=True)
# Assert we didn't go into PENDING_*
listener_check = self.mem_listener_client.show_listener(
listener[const.ID])
self.assertEqual(const.ACTIVE,
listener_check[const.PROVISIONING_STATUS])
self.assertFalse(listener_check[const.ADMIN_STATE_UP])
# Test that a user, without the load balancer member role, cannot
# update this listener
if not CONF.load_balancer.RBAC_test_type == const.NONE:
member2_client = self.os_roles_lb_member2.listener_client
self.assertRaises(exceptions.Forbidden,
member2_client.update_listener,
listener[const.ID], admin_state_up=True)
# Assert we didn't go into PENDING_*
listener_check = self.mem_listener_client.show_listener(
listener[const.ID])
self.assertEqual(const.ACTIVE,
listener_check[const.PROVISIONING_STATUS])
self.assertFalse(listener_check[const.ADMIN_STATE_UP])
listener_update_kwargs = {
const.NAME: new_name,
const.DESCRIPTION: new_description,
const.ADMIN_STATE_UP: True,
const.CONNECTION_LIMIT: 400,
const.TIMEOUT_CLIENT_DATA: 2000,
const.TIMEOUT_MEMBER_CONNECT: 2000,
const.TIMEOUT_MEMBER_DATA: 2000,
const.TIMEOUT_TCP_INSPECT: 100,
const.INSERT_HEADERS: {
const.X_FORWARDED_FOR: "false",
const.X_FORWARDED_PORT: "false"
},
# TODO(rm_work): need to finish the rest of this stuff
# const.DEFAULT_POOL_ID: '',
# const.DEFAULT_TLS_CONTAINER_REF: '',
# const.SNI_CONTAINER_REFS: [],
}
listener = self.mem_listener_client.update_listener(
listener[const.ID], **listener_update_kwargs)
listener = waiters.wait_for_status(
self.mem_listener_client.show_listener,
listener[const.ID], const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
self.assertEqual(new_name, listener[const.NAME])
self.assertEqual(new_description, listener[const.DESCRIPTION])
self.assertTrue(listener[const.ADMIN_STATE_UP])
self.assertEqual(400, listener[const.CONNECTION_LIMIT])
insert_headers = listener[const.INSERT_HEADERS]
self.assertFalse(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_FOR]))
self.assertFalse(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_PORT]))
self.assertEqual(2000, listener[const.TIMEOUT_CLIENT_DATA])
self.assertEqual(2000, listener[const.TIMEOUT_MEMBER_CONNECT])
self.assertEqual(2000, listener[const.TIMEOUT_MEMBER_DATA])
self.assertEqual(100, listener[const.TIMEOUT_TCP_INSPECT])
@decorators.idempotent_id('16f11c82-f069-4592-8954-81b35a98e3b7')
def test_listener_delete(self):
"""Tests listener create and delete APIs.
* Creates a listener.
* Validates that other accounts cannot delete the listener
* Deletes the listener.
* Validates the listener is in the DELETED state.
"""
listener_name = data_utils.rand_name("lb_member_listener1-delete")
listener_kwargs = {
const.NAME: listener_name,
const.PROTOCOL: const.HTTP,
const.PROTOCOL_PORT: 83,
const.LOADBALANCER_ID: self.lb_id,
}
listener = self.mem_listener_client.create_listener(**listener_kwargs)
self.addClassResourceCleanup(
self.mem_listener_client.cleanup_listener,
listener[const.ID],
lb_client=self.mem_lb_client, lb_id=self.lb_id)
waiters.wait_for_status(
self.mem_lb_client.show_loadbalancer,
self.lb_id, const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
# Test that a user without the load balancer role cannot
# delete this listener
if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
self.assertRaises(
exceptions.Forbidden,
self.os_primary.listener_client.delete_listener,
listener[const.ID])
# Test that a different user, with the load balancer member role
# cannot delete this listener
if not CONF.load_balancer.RBAC_test_type == const.NONE:
member2_client = self.os_roles_lb_member2.listener_client
self.assertRaises(exceptions.Forbidden,
member2_client.delete_listener,
listener[const.ID])
self.mem_listener_client.delete_listener(listener[const.ID])
waiters.wait_for_deleted_status_or_not_found(
self.mem_listener_client.show_listener, listener[const.ID],
const.PROVISIONING_STATUS,
CONF.load_balancer.check_interval,
CONF.load_balancer.check_timeout)

View File

@ -15,13 +15,13 @@
# under the License.
import testtools
import time
from uuid import UUID
from dateutil import parser
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from tempest.lib import exceptions
@ -82,8 +82,7 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
lb = self.mem_lb_client.create_loadbalancer(**lb_kwargs)
self.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
self.mem_lb_client.delete_loadbalancer,
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
lb = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
@ -141,8 +140,7 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
lb = self.mem_lb_client.create_loadbalancer(
name=lb_name, vip_network_id=self.lb_member_vip_net[const.ID])
self.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
self.mem_lb_client.delete_loadbalancer,
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
lb = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
@ -188,8 +186,7 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
lb = self.mem_lb_client.create_loadbalancer(
name=lb_name, vip_network_id=self.lb_member_vip_net[const.ID])
self.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
self.mem_lb_client.delete_loadbalancer,
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
lb = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
@ -264,8 +261,7 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
# vip_qos_policy_id=lb_qos_policy_id)
vip_network_id=self.lb_member_vip_net[const.ID])
self.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
self.mem_lb_client.delete_loadbalancer,
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
lb1 = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
@ -274,6 +270,10 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
const.ACTIVE,
CONF.load_balancer.lb_build_interval,
CONF.load_balancer.lb_build_timeout)
# Time resolution for created_at is only to the second, and we need to
# ensure that each object has a distinct creation time. Delaying one
# second is both a simple and a reliable way to accomplish this.
time.sleep(1)
lb_name = data_utils.rand_name("lb_member_lb1-list")
lb_description = 'A'
@ -284,8 +284,7 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
name=lb_name,
vip_network_id=self.lb_member_vip_net[const.ID])
self.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
self.mem_lb_client.delete_loadbalancer,
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
lb2 = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
@ -294,6 +293,10 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
const.ACTIVE,
CONF.load_balancer.lb_build_interval,
CONF.load_balancer.lb_build_timeout)
# Time resolution for created_at is only to the second, and we need to
# ensure that each object has a distinct creation time. Delaying one
# second is both a simple and a reliable way to accomplish this.
time.sleep(1)
lb_name = data_utils.rand_name("lb_member_lb3-list")
lb_description = 'C'
@ -304,8 +307,7 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
name=lb_name,
vip_network_id=self.lb_member_vip_net[const.ID])
self.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
self.mem_lb_client.delete_loadbalancer,
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
lb3 = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
@ -455,8 +457,7 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
lb = self.mem_lb_client.create_loadbalancer(**lb_kwargs)
self.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
self.mem_lb_client.delete_loadbalancer,
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
lb = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
@ -554,8 +555,7 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
lb = self.mem_lb_client.create_loadbalancer(**lb_kwargs)
self.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
self.mem_lb_client.delete_loadbalancer,
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
lb = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
@ -659,8 +659,7 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
lb = self.mem_lb_client.create_loadbalancer(
name=lb_name, vip_network_id=self.lb_member_vip_net[const.ID])
self.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
self.mem_lb_client.delete_loadbalancer,
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
lb = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
@ -721,8 +720,7 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
lb = self.mem_lb_client.create_loadbalancer(
name=lb_name, vip_network_id=self.lb_member_vip_net[const.ID])
self.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
self.mem_lb_client.delete_loadbalancer,
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
lb = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
@ -789,8 +787,7 @@ class LoadBalancerAPITest(test_base.LoadBalancerBaseTest):
lb = self.mem_lb_client.create_loadbalancer(
name=lb_name, vip_network_id=self.lb_member_vip_net[const.ID])
self.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
self.mem_lb_client.delete_loadbalancer,
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
lb = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,

View File

@ -0,0 +1,179 @@
# Copyright 2018 GoDaddy
#
# 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 uuid import UUID
from dateutil import parser
from oslo_utils import strutils
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from octavia_tempest_plugin.common import constants as const
from octavia_tempest_plugin.tests import test_base
from octavia_tempest_plugin.tests import waiters
CONF = config.CONF
class ListenerScenarioTest(test_base.LoadBalancerBaseTest):
@classmethod
def resource_setup(cls):
"""Setup resources needed by the tests."""
super(ListenerScenarioTest, cls).resource_setup()
lb_name = data_utils.rand_name("lb_member_lb1_listener")
lb_kwargs = {const.ADMIN_STATE_UP: False,
const.PROVIDER: CONF.load_balancer.provider,
const.NAME: lb_name}
ip_version = 6 if CONF.load_balancer.test_with_ipv6 else 4
cls._setup_lb_network_kwargs(lb_kwargs, ip_version)
lb = cls.mem_lb_client.create_loadbalancer(**lb_kwargs)
cls.lb_id = lb[const.ID]
cls.addClassResourceCleanup(
cls.mem_lb_client.cleanup_loadbalancer,
cls.lb_id)
waiters.wait_for_status(cls.mem_lb_client.show_loadbalancer,
cls.lb_id, const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.lb_build_interval,
CONF.load_balancer.lb_build_timeout)
@decorators.idempotent_id('4a874014-b7d1-49a4-ac9a-2400b3434700')
def test_listener_CRUD(self):
"""Tests listener create, read, update, delete
* Create a fully populated listener.
* Show listener details.
* Update the listener.
* Delete the listener.
"""
# Listener create
listener_name = data_utils.rand_name("lb_member_listener1-CRUD")
listener_description = data_utils.arbitrary_string(size=255)
listener_kwargs = {
const.NAME: listener_name,
const.DESCRIPTION: listener_description,
const.ADMIN_STATE_UP: False,
const.PROTOCOL: const.HTTP,
const.PROTOCOL_PORT: 80,
const.LOADBALANCER_ID: self.lb_id,
const.CONNECTION_LIMIT: 200,
const.TIMEOUT_CLIENT_DATA: 1000,
const.TIMEOUT_MEMBER_CONNECT: 1000,
const.TIMEOUT_MEMBER_DATA: 1000,
const.TIMEOUT_TCP_INSPECT: 50,
const.INSERT_HEADERS: {
const.X_FORWARDED_FOR: "true",
const.X_FORWARDED_PORT: "true"
},
# TODO(rm_work): need to finish the rest of this stuff
# const.DEFAULT_POOL_ID: '',
# const.DEFAULT_TLS_CONTAINER_REF: '',
# const.SNI_CONTAINER_REFS: [],
}
listener = self.mem_listener_client.create_listener(**listener_kwargs)
self.addClassResourceCleanup(
self.mem_listener_client.cleanup_listener,
listener[const.ID],
lb_client=self.mem_lb_client, lb_id=self.lb_id)
listener = waiters.wait_for_status(
self.mem_listener_client.show_listener,
listener[const.ID], const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
self.assertEqual(listener_name, listener[const.NAME])
self.assertEqual(listener_description, listener[const.DESCRIPTION])
self.assertFalse(listener[const.ADMIN_STATE_UP])
parser.parse(listener[const.CREATED_AT])
parser.parse(listener[const.UPDATED_AT])
UUID(listener[const.ID])
self.assertEqual(const.OFFLINE, listener[const.OPERATING_STATUS])
self.assertEqual(const.HTTP, listener[const.PROTOCOL])
self.assertEqual(80, listener[const.PROTOCOL_PORT])
self.assertEqual(200, listener[const.CONNECTION_LIMIT])
insert_headers = listener[const.INSERT_HEADERS]
self.assertTrue(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_FOR]))
self.assertTrue(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_PORT]))
self.assertEqual(1000, listener[const.TIMEOUT_CLIENT_DATA])
self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_CONNECT])
self.assertEqual(1000, listener[const.TIMEOUT_MEMBER_DATA])
self.assertEqual(50, listener[const.TIMEOUT_TCP_INSPECT])
# Listener update
new_name = data_utils.rand_name("lb_member_listener1-update")
new_description = data_utils.arbitrary_string(size=255,
base_text='new')
listener_update_kwargs = {
const.NAME: new_name,
const.DESCRIPTION: new_description,
const.ADMIN_STATE_UP: True,
const.CONNECTION_LIMIT: 400,
const.TIMEOUT_CLIENT_DATA: 2000,
const.TIMEOUT_MEMBER_CONNECT: 2000,
const.TIMEOUT_MEMBER_DATA: 2000,
const.TIMEOUT_TCP_INSPECT: 100,
const.INSERT_HEADERS: {
const.X_FORWARDED_FOR: "false",
const.X_FORWARDED_PORT: "false"
},
# TODO(rm_work): need to finish the rest of this stuff
# const.DEFAULT_POOL_ID: '',
# const.DEFAULT_TLS_CONTAINER_REF: '',
# const.SNI_CONTAINER_REFS: [],
}
listener = self.mem_listener_client.update_listener(
listener[const.ID], **listener_update_kwargs)
listener = waiters.wait_for_status(
self.mem_listener_client.show_listener,
listener[const.ID], const.PROVISIONING_STATUS,
const.ACTIVE,
CONF.load_balancer.build_interval,
CONF.load_balancer.build_timeout)
self.assertEqual(new_name, listener[const.NAME])
self.assertEqual(new_description, listener[const.DESCRIPTION])
self.assertTrue(listener[const.ADMIN_STATE_UP])
self.assertEqual(const.HTTP, listener[const.PROTOCOL])
self.assertEqual(80, listener[const.PROTOCOL_PORT])
self.assertEqual(400, listener[const.CONNECTION_LIMIT])
insert_headers = listener[const.INSERT_HEADERS]
self.assertFalse(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_FOR]))
self.assertFalse(
strutils.bool_from_string(insert_headers[const.X_FORWARDED_PORT]))
self.assertEqual(2000, listener[const.TIMEOUT_CLIENT_DATA])
self.assertEqual(2000, listener[const.TIMEOUT_MEMBER_CONNECT])
self.assertEqual(2000, listener[const.TIMEOUT_MEMBER_DATA])
self.assertEqual(100, listener[const.TIMEOUT_TCP_INSPECT])
# Listener delete
self.mem_listener_client.delete_listener(listener[const.ID])
waiters.wait_for_deleted_status_or_not_found(
self.mem_listener_client.show_listener, listener[const.ID],
const.PROVISIONING_STATUS,
CONF.load_balancer.check_interval,
CONF.load_balancer.check_timeout)

View File

@ -19,7 +19,6 @@ from dateutil import parser
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from octavia_tempest_plugin.common import constants as const
@ -61,8 +60,7 @@ class LoadBalancerScenarioTest(test_base.LoadBalancerBaseTest):
lb = self.mem_lb_client.create_loadbalancer(**lb_kwargs)
self.addClassResourceCleanup(
test_utils.call_and_ignore_notfound_exc,
self.mem_lb_client.delete_loadbalancer,
self.mem_lb_client.cleanup_loadbalancer,
lb[const.ID])
lb = waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
@ -113,8 +111,8 @@ class LoadBalancerScenarioTest(test_base.LoadBalancerBaseTest):
# Load balancer delete
self.mem_lb_client.delete_loadbalancer(lb[const.ID], cascade=True)
waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
lb[const.ID], const.PROVISIONING_STATUS,
const.DELETED,
CONF.load_balancer.lb_build_interval,
CONF.load_balancer.lb_build_timeout)
waiters.wait_for_deleted_status_or_not_found(
self.mem_lb_client.show_loadbalancer, lb[const.ID],
const.PROVISIONING_STATUS,
CONF.load_balancer.check_interval,
CONF.load_balancer.check_timeout)

View File

@ -26,7 +26,6 @@ from oslo_utils import uuidutils
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils.linux import remote_client
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions
from tempest import test
@ -104,6 +103,7 @@ class LoadBalancerBaseTest(test.BaseTestCase):
cls.lb_mem_servers_client = cls.os_roles_lb_member.servers_client
cls.lb_mem_subnet_client = cls.os_roles_lb_member.subnets_client
cls.mem_lb_client = cls.os_roles_lb_member.loadbalancer_client
cls.mem_listener_client = cls.os_roles_lb_member.listener_client
@classmethod
def resource_setup(cls):
@ -548,7 +548,6 @@ class LoadBalancerBaseTest(test.BaseTestCase):
subnet_id=cls.lb_member_1_subnet['id'])
cls.addClassResourceCleanup(
waiters.wait_for_not_found,
test_utils.call_and_ignore_notfound_exc,
cls.lb_mem_routers_client.remove_router_interface,
cls.lb_mem_routers_client.remove_router_interface,
cls.lb_member_router['id'], subnet_id=cls.lb_member_1_subnet['id'])

View File

@ -106,7 +106,7 @@ def wait_for_not_found(delete_func, show_func, *args, **kwargs):
:returns: None
"""
try:
return delete_func(*args, **kwargs)
delete_func(*args, **kwargs)
except exceptions.NotFound:
return
start = int(time.time())