101 lines
3.6 KiB
Python
101 lines
3.6 KiB
Python
# Copyright (c) 2021 Red Hat
|
|
# 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 __future__ import absolute_import
|
|
|
|
import collections
|
|
import typing
|
|
|
|
from oslo_log import log
|
|
|
|
import tobiko
|
|
from tobiko.openstack import octavia
|
|
from tobiko.shell import curl
|
|
from tobiko.shell import ssh
|
|
from tobiko.shell import sh
|
|
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
def check_members_balanced(ip_address: str,
|
|
protocol: str,
|
|
port: int,
|
|
pool_id: str = None,
|
|
members_count: int = None,
|
|
lb_algorithm: str = None,
|
|
requests_count: int = 10,
|
|
connect_timeout: tobiko.Seconds = 10.,
|
|
interval: tobiko.Seconds = 1,
|
|
ssh_client: ssh.SSHClientFixture = None) -> (
|
|
typing.Dict[str, int]):
|
|
|
|
"""Check if traffic is properly balanced between members."""
|
|
|
|
test_case = tobiko.get_test_case()
|
|
|
|
# Getting the members count
|
|
if members_count is None:
|
|
if pool_id is None:
|
|
raise ValueError('Either members_count or pool_id has to be passed'
|
|
' to the function.')
|
|
|
|
else: # members_count is None and pool_id is not None
|
|
members_count = len(octavia.list_members(pool_id=pool_id))
|
|
|
|
last_content = None
|
|
replies: typing.Dict[str, int] = collections.defaultdict(lambda: 0)
|
|
for attempt in tobiko.retry(count=members_count * requests_count,
|
|
interval=interval):
|
|
try:
|
|
content = curl.execute_curl(hostname=ip_address,
|
|
scheme=protocol,
|
|
port=port,
|
|
path='id',
|
|
connect_timeout=connect_timeout,
|
|
ssh_client=ssh_client).strip()
|
|
except sh.ShellCommandFailed as ex:
|
|
if ex.exit_status == 28:
|
|
raise octavia.TrafficTimeoutError(
|
|
reason=str(ex.stderr)) from ex
|
|
else:
|
|
raise ex
|
|
|
|
replies[content] += 1
|
|
|
|
if last_content is not None and lb_algorithm == 'ROUND_ROBIN':
|
|
if members_count > 1 and last_content == content:
|
|
raise octavia.RoundRobinException(
|
|
'Request was forwarded two times to the same host:\n'
|
|
f'members_count: {members_count}\n'
|
|
f'expected: {last_content}\n'
|
|
f'actual: {content}\n')
|
|
|
|
last_content = content
|
|
|
|
if attempt.is_last:
|
|
break
|
|
else:
|
|
raise RuntimeError('Broken retry loop')
|
|
|
|
LOG.debug(f"Replies counts from load balancer: {replies}")
|
|
|
|
# assert that 'members_count' servers replied
|
|
missing_members_count = members_count - len(replies)
|
|
test_case.assertEqual(0, missing_members_count,
|
|
f'Missing replies from {missing_members_count} "'
|
|
'"members.')
|
|
|
|
return replies
|