Merge "Fix health monitor API handling of None updates"

changes/65/667065/1
Zuul 4 years ago committed by Gerrit Code Review
commit b2d40c1120

@ -285,14 +285,13 @@ class HealthMonitorController(base.BaseController):
raise exceptions.InvalidOption(
value=consts.EXPECTED_CODES, option='health monitors of '
'type {}'.format(db_hm.type))
else:
# For HTTP health monitor these cannot be null/None
if health_monitor.http_method is None:
health_monitor.http_method = wtypes.Unset
if health_monitor.url_path is None:
health_monitor.url_path = wtypes.Unset
if health_monitor.expected_codes is None:
health_monitor.expected_codes = wtypes.Unset
if health_monitor.delay is None:
raise exceptions.InvalidOption(value=None, option=consts.DELAY)
if health_monitor.max_retries is None:
raise exceptions.InvalidOption(value=None,
option=consts.MAX_RETRIES)
if health_monitor.timeout is None:
raise exceptions.InvalidOption(value=None, option=consts.TIMEOUT)
if health_monitor.domain_name and not (
db_hm.http_version or health_monitor.http_version):
@ -308,6 +307,29 @@ class HealthMonitorController(base.BaseController):
value='http_version %s' % http_version,
option='health monitors HTTP 1.1 domain name health check')
def _set_default_on_none(self, health_monitor):
"""Reset settings to their default values if None/null was passed in
A None/null value can be passed in to clear a value. PUT values
that were not provided by the user have a type of wtypes.UnsetType.
If the user is attempting to clear values, they should either
be set to None (for example in the name field) or they should be
reset to their default values.
This method is intended to handle those values that need to be set
back to a default value.
"""
if health_monitor.http_method is None:
health_monitor.http_method = (
consts.HEALTH_MONITOR_HTTP_DEFAULT_METHOD)
if health_monitor.url_path is None:
health_monitor.url_path = (
consts.HEALTH_MONITOR_DEFAULT_URL_PATH)
if health_monitor.expected_codes is None:
health_monitor.expected_codes = (
consts.HEALTH_MONITOR_DEFAULT_EXPECTED_CODES)
if health_monitor.max_retries_down is None:
health_monitor.max_retries_down = consts.DEFAULT_MAX_RETRIES_DOWN
@wsme_pecan.wsexpose(hm_types.HealthMonitorRootResponse, wtypes.text,
body=hm_types.HealthMonitorRootPUT, status_code=200)
def put(self, id, health_monitor_):
@ -327,6 +349,9 @@ class HealthMonitorController(base.BaseController):
if (pool.protocol == consts.PROTOCOL_UDP and
db_hm.type == consts.HEALTH_MONITOR_UDP_CONNECT):
self._validate_healthmonitor_request_for_udp(health_monitor)
self._set_default_on_none(health_monitor)
# Load the driver early as it also provides validation
driver = driver_factory.get_driver(provider)

@ -86,7 +86,8 @@ class HealthMonitorPOST(BaseHealthMonitorType):
timeout = wtypes.wsattr(wtypes.IntegerType(minimum=0), mandatory=True)
max_retries_down = wtypes.wsattr(
wtypes.IntegerType(minimum=constants.MIN_HM_RETRIES,
maximum=constants.MAX_HM_RETRIES), default=3)
maximum=constants.MAX_HM_RETRIES),
default=constants.DEFAULT_MAX_RETRIES_DOWN)
max_retries = wtypes.wsattr(
wtypes.IntegerType(minimum=constants.MIN_HM_RETRIES,
maximum=constants.MAX_HM_RETRIES),
@ -152,7 +153,8 @@ class HealthMonitorSingleCreate(BaseHealthMonitorType):
timeout = wtypes.wsattr(wtypes.IntegerType(minimum=0), mandatory=True)
max_retries_down = wtypes.wsattr(
wtypes.IntegerType(minimum=constants.MIN_HM_RETRIES,
maximum=constants.MAX_HM_RETRIES), default=3)
maximum=constants.MAX_HM_RETRIES),
default=constants.DEFAULT_MAX_RETRIES_DOWN)
max_retries = wtypes.wsattr(
wtypes.IntegerType(minimum=constants.MIN_HM_RETRIES,
maximum=constants.MAX_HM_RETRIES),

@ -193,11 +193,14 @@ HEALTH_MONITOR_DEFAULT_URL_PATH = '/'
TYPE = 'type'
URL_PATH = 'url_path'
HTTP_METHOD = 'http_method'
HTTP_VERSION = 'http_version'
EXPECTED_CODES = 'expected_codes'
DELAY = 'delay'
TIMEOUT = 'timeout'
MAX_RETRIES = 'max_retries'
MAX_RETRIES_DOWN = 'max_retries_down'
RISE_THRESHOLD = 'rise_threshold'
DOMAIN_NAME = 'domain_name'
UPDATE_STATS = 'UPDATE_STATS'
UPDATE_HEALTH = 'UPDATE_HEALTH'
@ -212,6 +215,7 @@ MIN_CONNECTION_LIMIT = -1
MIN_WEIGHT = 0
MAX_WEIGHT = 256
DEFAULT_MAX_RETRIES_DOWN = 3
MIN_HM_RETRIES = 1
MAX_HM_RETRIES = 10

@ -1519,6 +1519,42 @@ class TestHealthMonitor(base.BaseAPITest):
self.put(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
self._build_body(new_hm), status=400)
def test_update_delay_none(self):
api_hm = self.create_health_monitor(self.pool_with_listener_id,
constants.HEALTH_MONITOR_HTTP,
1, 1, 1, 1).get(self.root_tag)
new_hm = {constants.DELAY: None}
self.set_lb_status(self.lb_id)
expect_error_msg = ("None is not a valid option for %s" %
constants.DELAY)
res = self.put(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
self._build_body(new_hm), status=400)
self.assertEqual(expect_error_msg, res.json['faultstring'])
def test_update_max_retries_none(self):
api_hm = self.create_health_monitor(self.pool_with_listener_id,
constants.HEALTH_MONITOR_HTTP,
1, 1, 1, 1).get(self.root_tag)
new_hm = {constants.MAX_RETRIES: None}
self.set_lb_status(self.lb_id)
expect_error_msg = ("None is not a valid option for %s" %
constants.MAX_RETRIES)
res = self.put(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
self._build_body(new_hm), status=400)
self.assertEqual(expect_error_msg, res.json['faultstring'])
def test_update_timeout_none(self):
api_hm = self.create_health_monitor(self.pool_with_listener_id,
constants.HEALTH_MONITOR_HTTP,
1, 1, 1, 1).get(self.root_tag)
new_hm = {constants.TIMEOUT: None}
self.set_lb_status(self.lb_id)
expect_error_msg = ("None is not a valid option for %s" %
constants.TIMEOUT)
res = self.put(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
self._build_body(new_hm), status=400)
self.assertEqual(expect_error_msg, res.json['faultstring'])
@mock.patch('octavia.api.drivers.utils.call_provider')
def test_update_with_bad_provider(self, mock_provider):
api_hm = self.create_health_monitor(
@ -1631,6 +1667,31 @@ class TestHealthMonitor(base.BaseAPITest):
'domain_name'], constants.DOMAIN_NAME_REGEX)
self.assertEqual(expect_error_msg, response.json['faultstring'])
def test_update_unset_defaults(self):
api_hm = self.create_health_monitor(
self.pool_with_listener_id, constants.HEALTH_MONITOR_HTTP,
1, 1, 1, 1, name='test', domain_name='test.example.com',
expected_codes='400', http_method='HEAD', http_version='1.1',
url_path='/test').get(self.root_tag)
new_hm = {constants.DOMAIN_NAME: None, constants.EXPECTED_CODES: None,
constants.HTTP_METHOD: None, constants.HTTP_VERSION: None,
constants.MAX_RETRIES_DOWN: None, 'name': None,
constants.URL_PATH: None}
self.set_lb_status(self.lb_id)
res = self.put(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
self._build_body(new_hm)).json.get(self.root_tag)
self.assertIsNone(res[constants.DOMAIN_NAME])
self.assertEqual(constants.HEALTH_MONITOR_DEFAULT_EXPECTED_CODES,
res[constants.EXPECTED_CODES])
self.assertEqual(constants.HEALTH_MONITOR_HTTP_DEFAULT_METHOD,
res[constants.HTTP_METHOD])
self.assertIsNone(res[constants.HTTP_VERSION])
self.assertEqual(constants.DEFAULT_MAX_RETRIES_DOWN,
res[constants.MAX_RETRIES_DOWN])
self.assertEqual('', res['name'])
self.assertEqual(constants.HEALTH_MONITOR_DEFAULT_URL_PATH,
res[constants.URL_PATH])
def test_delete(self):
api_hm = self.create_health_monitor(
self.pool_with_listener_id,

Loading…
Cancel
Save