Neutron LBaaS: Health Monitor Behaviors
* includes initial BaseLoadBalancersBehaviors class * commonly used methods for creating, updating and waiting * contains metatests for de/serialzation in json/xml Change-Id: Ic4a0704013cb8028958b7a7e3d5ffd6d6b407564
This commit is contained in:
parent
b93c7e4cd1
commit
ce899248a9
|
@ -0,0 +1,176 @@
|
|||
"""
|
||||
Copyright 2014 Rackspace
|
||||
|
||||
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 cafe.engine.behaviors import BaseBehavior
|
||||
|
||||
from cloudcafe.common.exceptions import BuildErrorException, TimeoutException
|
||||
from cloudcafe.networking.lbaas.common.types import LBaaSStatusTypes
|
||||
|
||||
|
||||
class BaseLoadBalancersBehaviors(BaseBehavior):
|
||||
|
||||
def __init__(self, lbaas_client_type, config):
|
||||
super(BaseLoadBalancersBehaviors, self).__init__()
|
||||
self.config = config
|
||||
|
||||
# This can be a be client of type: health_monitor, listener,
|
||||
# load_balancer, member or pool
|
||||
self.lbaas_client_type = lbaas_client_type
|
||||
|
||||
def create_active_lbaas_object(self, lbaas_model_type, **kwargs):
|
||||
"""
|
||||
@summary: Creates a LBaaS object and waits for it to become active
|
||||
@param lbaas_model_type: The type of the LBaaS object.
|
||||
ex: health_monitor, listener, load_balancer, member, pool
|
||||
@type lbaas_model_type: str
|
||||
@param kwargs: Key/value pairs to be used in
|
||||
create function call
|
||||
@type kwargs: dict
|
||||
@return: Response object containing response and the LBaaS model
|
||||
domain object
|
||||
@rtype: requests.Response
|
||||
"""
|
||||
|
||||
try:
|
||||
create_func = getattr(self.lbaas_client_type,
|
||||
"create_{0}".format(lbaas_model_type))
|
||||
except AttributeError as ex:
|
||||
error_message = ("Failed to obtain 'create' method for {type_}: "
|
||||
"{message}".format(type_=lbaas_model_type,
|
||||
message=ex.message))
|
||||
self._log.error(error_message)
|
||||
raise Exception(error_message)
|
||||
else:
|
||||
resp = create_func(**kwargs)
|
||||
assert resp.status_code == 202
|
||||
assert resp.entity is not None
|
||||
|
||||
lbaas_model_obj = resp.entity
|
||||
resp = self.wait_for_lbaas_object_status(
|
||||
lbaas_object_id=lbaas_model_obj.entity.id,
|
||||
lbaas_model_type=lbaas_model_type,
|
||||
desired_status=LBaaSStatusTypes.ACTIVE)
|
||||
return resp
|
||||
|
||||
def update_lbaas_object_and_wait_for_active(
|
||||
self, lbaas_model_type, **kwargs):
|
||||
"""
|
||||
@summary: Updates a LBaaS object and waits for it to become active
|
||||
@param lbaas_object_id: The id of the LBaaS type object.
|
||||
@type lbaas_object_id: str
|
||||
@param lbaas_model_type: The type of the LBaaS object.
|
||||
ex: health_monitor, listener, load_balancer, member, pool
|
||||
@type lbaas_model_type: str
|
||||
@param kwargs: Key/value pairs to be used in
|
||||
create function call
|
||||
@type kwargs: dict
|
||||
@return: Response object containing response and the image
|
||||
domain object
|
||||
@rtype: requests.Response
|
||||
"""
|
||||
|
||||
try:
|
||||
update_func = getattr(self.lbaas_client_type,
|
||||
"update_{0}".format(lbaas_model_type))
|
||||
except AttributeError as ex:
|
||||
error_message = ("Failed to obtain 'update' method for {type_}: "
|
||||
"{message}".format(type_=lbaas_model_type,
|
||||
message=ex.message))
|
||||
self._log.error(error_message)
|
||||
raise Exception(error_message)
|
||||
else:
|
||||
resp = update_func(**kwargs)
|
||||
assert resp.status_code == 202
|
||||
assert resp.entity is not None
|
||||
|
||||
lbaas_model_obj = resp.entity
|
||||
resp = self.wait_for_lbaas_object_status(
|
||||
lbaas_object_id=lbaas_model_obj.entity.id,
|
||||
lbaas_model_type=lbaas_model_type,
|
||||
desired_status=LBaaSStatusTypes.ACTIVE)
|
||||
return resp
|
||||
|
||||
def wait_for_lbaas_object_status(self, lbaas_object_id, lbaas_model_type,
|
||||
desired_status, interval_time=None,
|
||||
timeout=None):
|
||||
"""
|
||||
@summary: Waits for a LBaaS type object to reach a desired status.
|
||||
@param lbaas_object_id: The id of the LBaaS type object.
|
||||
@type lbaas_object_id: str
|
||||
@param lbaas_model_type: The type of the LBaaS object.
|
||||
ex: health_monitor, listener, load_balancer, member, pool
|
||||
@type lbaas_model_type: str
|
||||
@param desired_status: Desired final status of the LBaaS type object.
|
||||
@type desired_status: str
|
||||
@param interval_time: Amount of time in seconds to wait
|
||||
between polling.
|
||||
@type interval_time: int
|
||||
@param interval_time: Amount of time in seconds to wait
|
||||
before aborting.
|
||||
@type interval_time: int
|
||||
@return: Response object containing response and the LBaaS type
|
||||
domain object.
|
||||
@rtype: requests.Response
|
||||
"""
|
||||
|
||||
interval_time = interval_time or self.config.lbaas_status_interval
|
||||
timeout = timeout or self.config.lbaas_timeout
|
||||
end_time = time.time() + timeout
|
||||
|
||||
while time.time() < end_time:
|
||||
try:
|
||||
get_func = getattr(self.lbaas_client_type,
|
||||
"get_{0}".format(lbaas_model_type))
|
||||
except AttributeError as ex:
|
||||
error_message = ("Failed to obtain 'get' method for {type_}: "
|
||||
"{message}".format(type_=lbaas_model_type,
|
||||
message=ex.message))
|
||||
self._log.error(error_message)
|
||||
raise Exception(error_message)
|
||||
else:
|
||||
resp = get_func(lbaas_object_id)
|
||||
|
||||
if not resp.ok:
|
||||
raise Exception(
|
||||
"Failed to get {type_} information: "
|
||||
"{code} - {reason}".format(type_=lbaas_model_type,
|
||||
code=resp.status_code,
|
||||
reason=resp.reason))
|
||||
if resp.entity is None:
|
||||
raise Exception(
|
||||
"Response entity was not set. "
|
||||
"Response was: {0}".format(resp.content))
|
||||
|
||||
lbaas_object = resp.entity
|
||||
|
||||
if lbaas_object.status.lower() == LBaaSStatusTypes.ERROR.lower():
|
||||
raise BuildErrorException(
|
||||
'Failed during wait of {type_} status with id {id_} '
|
||||
'entered ERROR status.'.format(type_=lbaas_model_type,
|
||||
id_=lbaas_object.id))
|
||||
|
||||
if lbaas_object.status == desired_status:
|
||||
break
|
||||
time.sleep(interval_time)
|
||||
else:
|
||||
raise TimeoutException(
|
||||
"wait for {type_} ran for {timeout} seconds and did not "
|
||||
"observe {type_} {id_} reach the {status} status.".format(
|
||||
type_=lbaas_model_type, timeout=timeout,
|
||||
id_=lbaas_object_id, status=desired_status))
|
||||
return resp
|
|
@ -14,5 +14,126 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
"""
|
||||
|
||||
from cafe.engine.behaviors import BaseBehavior
|
||||
from cloudcafe.networking.lbaas.common.behaviors import \
|
||||
BaseLoadBalancersBehaviors
|
||||
|
||||
|
||||
class HealthMonitorBehaviors(BaseLoadBalancersBehaviors):
|
||||
|
||||
OBJECT_MODEL = 'health_monitor'
|
||||
|
||||
def __init__(self, health_monitors_client, config):
|
||||
super(HealthMonitorBehaviors, self).__init__(
|
||||
lbaas_client_type=health_monitors_client, config=config)
|
||||
|
||||
def create_active_health_monitor(
|
||||
self, type_, tenant_id, delay, timeout, max_retries,
|
||||
http_method=None, url_path=None, expected_codes=None,
|
||||
admin_state_up=None):
|
||||
"""
|
||||
@summary: Creates a health monitor and waits for it to become active
|
||||
@param type_: Protocol used for health monitor.
|
||||
e.g., HTTP, HTTPS, TCP, PING
|
||||
@type type_: str
|
||||
@param tenant_id: Tenant that owns the health monitor.
|
||||
@type tenant_id: str
|
||||
@param delay: Time in seconds between probes.
|
||||
@type delay: int
|
||||
@param timeout: Time in seconds to timeout each probe.
|
||||
@type timeout: int
|
||||
@param max_retries: Maximum consecutive health probe tries.
|
||||
@type max_retries: int
|
||||
@param http_method: HTTP method monitor uses to make request.
|
||||
Default: "GET"
|
||||
@type http_method: str
|
||||
@param url_path: Path portion of URI that will be probed if
|
||||
type is HTTP(S).
|
||||
Default: "/"
|
||||
@type url_path: str
|
||||
@param expected_codes: Expected HTTP codes for a passing HTTP(S).
|
||||
Default: "200"
|
||||
@type expected_codes: str
|
||||
@param admin_state_up: Enabled or Disabled
|
||||
Default: "true"
|
||||
@type admin_state_up: bool
|
||||
@return: Response object containing response and the health monitor
|
||||
domain object
|
||||
@rtype: requests.Response
|
||||
"""
|
||||
kwargs = {'type_': type_, 'tenant_id': tenant_id, 'delay': delay,
|
||||
'timeout': timeout, 'max_retries': max_retries,
|
||||
'http_method': http_method, 'url_path': url_path,
|
||||
'expected_codes': expected_codes,
|
||||
'admin_state_up': admin_state_up}
|
||||
resp = self.create_active_lbaas_object(
|
||||
lbaas_model_type=self.OBJECT_MODEL,
|
||||
kwargs=kwargs)
|
||||
return resp
|
||||
|
||||
def update_health_monitor_and_wait_for_active(
|
||||
self, health_monitor_id, delay=None,
|
||||
timeout=None, max_retries=None, http_method=None,
|
||||
url_path=None, expected_codes=None,
|
||||
admin_state_up=None):
|
||||
"""
|
||||
@summary: Updates a health monitor and waits for it to become active
|
||||
@param health_monitor_id: ID of the health monitor to update.
|
||||
@type health_monitor_id: str
|
||||
@param delay: Time in seconds between probes.
|
||||
@type delay: int
|
||||
@param timeout: Time in seconds to timeout each probe.
|
||||
@type timeout: int
|
||||
@param max_retries: Maximum consecutive health probe tries.
|
||||
@type max_retries: int
|
||||
@param http_method: HTTP method monitor uses to make request.
|
||||
Default: "GET"
|
||||
@type http_method: str
|
||||
@param url_path: Path portion of URI that will be probed if
|
||||
type is HTTP(S).
|
||||
Default: "/"
|
||||
@type url_path: str
|
||||
@param expected_codes: Expected HTTP codes for a passing HTTP(S).
|
||||
Default: "200"
|
||||
@type expected_codes: str
|
||||
@param admin_state_up: Enabled or Disabled
|
||||
Default: "true"
|
||||
@type admin_state_up: bool
|
||||
@return: Response object containing response and the health monitor
|
||||
domain object
|
||||
@rtype: requests.Response
|
||||
"""
|
||||
kwargs = {'health_monitor_id': health_monitor_id,
|
||||
'delay': delay, 'timeout': timeout,
|
||||
'max_retries': max_retries, 'http_method': http_method,
|
||||
'url_path': url_path, 'expected_codes': expected_codes,
|
||||
'admin_state_up': admin_state_up}
|
||||
resp = self.update_lbaas_object_and_wait_for_active(
|
||||
lbaas_model_type=self.OBJECT_MODEL,
|
||||
kwargs=kwargs)
|
||||
return resp
|
||||
|
||||
def wait_for_health_monitor_status(self, health_monitor_id, desired_status,
|
||||
interval_time=None, timeout=None):
|
||||
"""
|
||||
@summary: Waits for a health monitor to reach a desired status
|
||||
@param health_monitor_id: The id of the health monitor
|
||||
@type health_monitor_id: String
|
||||
@param desired_status: The desired final status of the health monitor
|
||||
@type desired_status: String
|
||||
@param interval_time: The amount of time in seconds to wait
|
||||
between polling
|
||||
@type interval_time: Integer
|
||||
@param interval_time: The amount of time in seconds to wait
|
||||
before aborting
|
||||
@type interval_time: Integer
|
||||
@return: Response object containing response and the health monitor
|
||||
domain object
|
||||
@rtype: requests.Response
|
||||
"""
|
||||
kwargs = {'health_monitor_id': health_monitor_id,
|
||||
'desired_status': desired_status,
|
||||
'interval_time': interval_time,
|
||||
'timeout': timeout}
|
||||
resp = self.wait_for_lbaas_object_status(
|
||||
lbaas_model_type=self.OBJECT_MODEL, **kwargs)
|
||||
return resp
|
||||
|
|
|
@ -14,5 +14,117 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
"""
|
||||
|
||||
from cafe.engine.behaviors import BaseBehavior
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from cloudcafe.networking.lbaas.lbaas_api.health_monitor.behaviors \
|
||||
import HealthMonitorBehaviors
|
||||
from cloudcafe.networking.lbaas.lbaas_api.health_monitor.client \
|
||||
import HealthMonitorsClient
|
||||
|
||||
|
||||
class HealthMonitorBehaviorsFixture(unittest.TestCase):
|
||||
"""
|
||||
@summary: Health Monitor Behaviors Tests
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(HealthMonitorBehaviorsFixture, cls).setUpClass()
|
||||
|
||||
cls.auth_token = "fake_auth_token"
|
||||
cls.url = "http://fake.url.endpoint"
|
||||
cls.health_monitor_id = "12345"
|
||||
cls.type_ = "HTTP"
|
||||
cls.tenant_id = "453105b9-1754-413f-aab1-55f1af620750"
|
||||
cls.delay = 20
|
||||
cls.timeout = 10
|
||||
cls.max_retries = 5
|
||||
cls.http_method = "GET"
|
||||
cls.url_path = "/check"
|
||||
cls.expected_codes = "200-299"
|
||||
cls.admin_state_up = False
|
||||
|
||||
cls.desired_status = "ACTIVE"
|
||||
cls.interval_time = 20
|
||||
cls.timeout = 120
|
||||
|
||||
cls.health_monitors_client = HealthMonitorsClient(
|
||||
url=cls.url,
|
||||
auth_token=cls.auth_token,
|
||||
serialize_format=cls.SERIALIZE,
|
||||
deserialize_format=cls.DESERIALIZE)
|
||||
|
||||
cls.health_monitor_behaviors = HealthMonitorBehaviors(
|
||||
health_monitors_client=cls.health_monitors_client, config=None)
|
||||
|
||||
|
||||
class HealthMonitorBehaviorsTests(object):
|
||||
|
||||
@mock.patch.object(HealthMonitorBehaviors, 'create_active_health_monitor',
|
||||
autospec=True)
|
||||
def test_create_active_health_monitor(self, mock_request):
|
||||
|
||||
create_active_health_monitor_kwargs = (
|
||||
{'type_': self.type_,
|
||||
'tenant_id': self.tenant_id,
|
||||
'delay': self.delay,
|
||||
'timeout': self.timeout,
|
||||
'max_retries': self.max_retries,
|
||||
'http_method': self.http_method,
|
||||
'url_path': self.url_path,
|
||||
'expected_codes': self.expected_codes,
|
||||
'admin_state_up': self.admin_state_up})
|
||||
self.health_monitor_behaviors.create_active_health_monitor(
|
||||
**create_active_health_monitor_kwargs)
|
||||
mock_request.assert_called_once_with(
|
||||
self.health_monitor_behaviors,
|
||||
**create_active_health_monitor_kwargs)
|
||||
|
||||
@mock.patch.object(HealthMonitorBehaviors,
|
||||
'update_health_monitor_and_wait_for_active',
|
||||
autospec=True)
|
||||
def test_update_health_monitor_and_wait_for_active(self, mock_request):
|
||||
|
||||
update_health_monitor_and_wait_for_active_kwargs = (
|
||||
{'health_monitor_id': self.health_monitor_id,
|
||||
'delay': self.delay,
|
||||
'timeout': self.timeout,
|
||||
'max_retries': self.max_retries,
|
||||
'http_method': self.http_method,
|
||||
'url_path': self.url_path,
|
||||
'expected_codes': self.expected_codes,
|
||||
'admin_state_up': self.admin_state_up})
|
||||
self.health_monitor_behaviors.\
|
||||
update_health_monitor_and_wait_for_active(
|
||||
**update_health_monitor_and_wait_for_active_kwargs)
|
||||
mock_request.assert_called_once_with(
|
||||
self.health_monitor_behaviors,
|
||||
**update_health_monitor_and_wait_for_active_kwargs)
|
||||
|
||||
@mock.patch.object(HealthMonitorBehaviors,
|
||||
'wait_for_health_monitor_status',
|
||||
autospec=True)
|
||||
def test_wait_for_health_monitor_status(self, mock_request):
|
||||
|
||||
wait_for_health_monitor_status_kwargs = (
|
||||
{'health_monitor_id': self.health_monitor_id,
|
||||
'desired_status': self.desired_status,
|
||||
'interval_time': self.interval_time,
|
||||
'timeout': self.timeout})
|
||||
self.health_monitor_behaviors.wait_for_health_monitor_status(
|
||||
**wait_for_health_monitor_status_kwargs)
|
||||
mock_request.assert_called_once_with(
|
||||
self.health_monitor_behaviors,
|
||||
**wait_for_health_monitor_status_kwargs)
|
||||
|
||||
|
||||
class HealthMonitorsClientTestsXML(HealthMonitorBehaviorsFixture,
|
||||
HealthMonitorBehaviorsTests):
|
||||
SERIALIZE = 'xml'
|
||||
DESERIALIZE = 'xml'
|
||||
|
||||
|
||||
class HealthMonitorsClientTestsJSON(HealthMonitorBehaviorsFixture,
|
||||
HealthMonitorBehaviorsTests):
|
||||
SERIALIZE = 'json'
|
||||
DESERIALIZE = 'json'
|
||||
|
|
Loading…
Reference in New Issue