Introduce Octavia v2 API for Health Monitor
GET all - /v2.0/lbaas/healthmonitors GET one - /v2.0/lbaas/healthmonitors/<hm_id> POST - /v2.0/lbaas/healthmonitors {<body>} PUT - /v2.0/lbaas/healthmonitors<hm_id> {<body>} DELETE - /v2.0/lbaas/healthmonitors/<hm_id> Co-Authored-By: Sindhu Devale <sindhu.devale@intel.com> Co-Authored-By: Adam Harwell <flux.adam@gmail.com> Co-Authored-By: Reedip Banerjee <reedip14@gmail.com> Partial-Bug: #1616643 Change-Id: I7f65bb9370530ae3b533b927fcdabc6c1a295231
This commit is contained in:
parent
13730f5bfe
commit
7ea7888b0d
|
@ -47,10 +47,13 @@ class URLType(wtypes.UserType):
|
|||
basetype = unicode
|
||||
name = 'url'
|
||||
|
||||
@staticmethod
|
||||
def validate(value):
|
||||
def __init__(self, require_scheme=True):
|
||||
super(URLType, self).__init__()
|
||||
self.require_scheme = require_scheme
|
||||
|
||||
def validate(self, value):
|
||||
try:
|
||||
validate.url(value)
|
||||
validate.url(value, require_scheme=self.require_scheme)
|
||||
except exceptions.InvalidURL:
|
||||
error = 'Value must be a valid URL string'
|
||||
raise ValueError(error)
|
||||
|
|
|
@ -16,6 +16,7 @@ from wsme import types as wtypes
|
|||
from wsmeext import pecan as wsme_pecan
|
||||
|
||||
from octavia.api.v2.controllers import base
|
||||
from octavia.api.v2.controllers import health_monitor
|
||||
from octavia.api.v2.controllers import l7policy
|
||||
from octavia.api.v2.controllers import listener
|
||||
from octavia.api.v2.controllers import load_balancer
|
||||
|
@ -27,6 +28,7 @@ class BaseV2Controller(base.BaseController):
|
|||
listeners = None
|
||||
pools = None
|
||||
l7policies = None
|
||||
healthmonitors = None
|
||||
|
||||
def __init__(self):
|
||||
super(BaseV2Controller, self).__init__()
|
||||
|
@ -34,6 +36,7 @@ class BaseV2Controller(base.BaseController):
|
|||
self.listeners = listener.ListenersController()
|
||||
self.pools = pool.PoolsController()
|
||||
self.l7policies = l7policy.L7PolicyController()
|
||||
self.healthmonitors = health_monitor.HealthMonitorController()
|
||||
|
||||
@wsme_pecan.wsexpose(wtypes.text)
|
||||
def get(self):
|
||||
|
|
|
@ -0,0 +1,239 @@
|
|||
# Copyright 2014 Rackspace
|
||||
# Copyright 2016 Blue Box, an IBM Company
|
||||
#
|
||||
# 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 logging
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_db import exception as odb_exceptions
|
||||
from oslo_utils import excutils
|
||||
import pecan
|
||||
from wsme import types as wtypes
|
||||
from wsmeext import pecan as wsme_pecan
|
||||
|
||||
from octavia.api.v2.controllers import base
|
||||
from octavia.api.v2.types import health_monitor as hm_types
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import api as db_api
|
||||
from octavia.db import prepare as db_prepare
|
||||
from octavia.i18n import _LI
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HealthMonitorController(base.BaseController):
|
||||
|
||||
def __init__(self):
|
||||
super(HealthMonitorController, self).__init__()
|
||||
self.handler = self.handler.health_monitor
|
||||
|
||||
def _get_db_hm(self, session, hm_id):
|
||||
"""Gets the current health monitor object from the database."""
|
||||
db_hm = self.repositories.health_monitor.get(
|
||||
session, id=hm_id)
|
||||
if not db_hm:
|
||||
LOG.info(_LI("Health Monitor %s was not found"), hm_id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.HealthMonitor._name(),
|
||||
id=hm_id)
|
||||
return db_hm
|
||||
|
||||
@wsme_pecan.wsexpose(hm_types.HealthMonitorRootResponse, wtypes.text,
|
||||
wtypes.text)
|
||||
def get_one(self, id):
|
||||
"""Gets a single healthmonitor's details."""
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
db_hm = self._get_db_hm(context.session, id)
|
||||
result = self._convert_db_to_type(
|
||||
db_hm, hm_types.HealthMonitorResponse)
|
||||
return hm_types.HealthMonitorRootResponse(healthmonitor=result)
|
||||
|
||||
@wsme_pecan.wsexpose(hm_types.HealthMonitorsRootResponse, wtypes.text,
|
||||
wtypes.text)
|
||||
def get_all(self, tenant_id=None, project_id=None):
|
||||
"""Gets a single health monitor's details."""
|
||||
# NOTE(blogan): since a pool can only have one health monitor
|
||||
# we are using the get_all method to only get the single health monitor
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
if context.is_admin or CONF.auth_strategy == constants.NOAUTH:
|
||||
if project_id or tenant_id:
|
||||
project_id = {'project_id': project_id or tenant_id}
|
||||
else:
|
||||
project_id = {}
|
||||
else:
|
||||
project_id = {'project_id': context.project_id}
|
||||
db_hm = self.repositories.health_monitor.get_all(
|
||||
context.session, **project_id)
|
||||
result = self._convert_db_to_type(
|
||||
db_hm, [hm_types.HealthMonitorResponse])
|
||||
return hm_types.HealthMonitorsRootResponse(healthmonitors=result)
|
||||
|
||||
def _get_affected_listener_ids(self, session, hm):
|
||||
"""Gets a list of all listeners this request potentially affects."""
|
||||
pool = self.repositories.pool.get(session, id=hm.pool_id)
|
||||
listener_ids = [l.id for l in pool.listeners]
|
||||
return listener_ids
|
||||
|
||||
def _test_lb_and_listener_and_pool_statuses(self, session, hm):
|
||||
"""Verify load balancer is in a mutable state."""
|
||||
# We need to verify that any listeners referencing this pool are also
|
||||
# mutable
|
||||
pool = self.repositories.pool.get(session, id=hm.pool_id)
|
||||
load_balancer_id = pool.load_balancer_id
|
||||
if not self.repositories.test_and_set_lb_and_listeners_prov_status(
|
||||
session, load_balancer_id,
|
||||
constants.PENDING_UPDATE, constants.PENDING_UPDATE,
|
||||
listener_ids=self._get_affected_listener_ids(session, hm),
|
||||
pool_id=hm.pool_id):
|
||||
LOG.info(_LI("Health Monitor cannot be created or modified "
|
||||
"because the Load Balancer is in an immutable state"))
|
||||
raise exceptions.ImmutableObject(resource='Load Balancer',
|
||||
id=load_balancer_id)
|
||||
|
||||
def _reset_lb_listener_pool_statuses(self, session, hm):
|
||||
# Setting LB + listener + pool back to active because this should be a
|
||||
# recoverable error
|
||||
pool = self._get_db_pool(session, hm.pool_id)
|
||||
load_balancer_id = pool.load_balancer_id
|
||||
self.repositories.load_balancer.update(
|
||||
session, load_balancer_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
for listener in self._get_affected_listener_ids(session, hm):
|
||||
self.repositories.listener.update(
|
||||
session, listener,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
self.repositories.pool.update(session, hm.pool_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
|
||||
def _validate_create_hm(self, lock_session, hm_dict):
|
||||
"""Validate creating health monitor on pool."""
|
||||
try:
|
||||
return self.repositories.health_monitor.create(
|
||||
lock_session, **hm_dict)
|
||||
except odb_exceptions.DBDuplicateEntry as de:
|
||||
if ['id'] == de.columns:
|
||||
raise exceptions.IDAlreadyExists()
|
||||
except odb_exceptions.DBError:
|
||||
# TODO(blogan): will have to do separate validation protocol
|
||||
# before creation or update since the exception messages
|
||||
# do not give any information as to what constraint failed
|
||||
raise exceptions.InvalidOption(value='', option='')
|
||||
|
||||
def _send_hm_to_handler(self, session, db_hm):
|
||||
try:
|
||||
LOG.info(_LI("Sending Creation of Health Monitor %s to handler"),
|
||||
db_hm.id)
|
||||
self.handler.create(db_hm)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(
|
||||
reraise=False), db_api.get_lock_session() as lock_session:
|
||||
self._reset_lb_listener_pool_statuses(
|
||||
lock_session, db_hm)
|
||||
# Health Monitor now goes to ERROR
|
||||
self.repositories.health_monitor.update(
|
||||
lock_session, db_hm.id,
|
||||
provisioning_status=constants.ERROR)
|
||||
db_hm = self._get_db_hm(session, db_hm.id)
|
||||
result = self._convert_db_to_type(
|
||||
db_hm, hm_types.HealthMonitorResponse)
|
||||
return hm_types.HealthMonitorRootResponse(healthmonitor=result)
|
||||
|
||||
@wsme_pecan.wsexpose(hm_types.HealthMonitorRootResponse,
|
||||
body=hm_types.HealthMonitorRootPOST, status_code=201)
|
||||
def post(self, health_monitor_):
|
||||
"""Creates a health monitor on a pool."""
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
health_monitor = health_monitor_.healthmonitor
|
||||
pool = self._get_db_pool(context.session, health_monitor.pool_id)
|
||||
health_monitor.project_id = pool.project_id
|
||||
|
||||
lock_session = db_api.get_session(autocommit=False)
|
||||
|
||||
hm_dict = db_prepare.create_health_monitor(
|
||||
health_monitor.to_dict(render_unsets=True))
|
||||
try:
|
||||
self._test_lb_and_listener_and_pool_statuses(
|
||||
lock_session, health_monitor)
|
||||
db_hm = self._validate_create_hm(lock_session, hm_dict)
|
||||
lock_session.commit()
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
lock_session.rollback()
|
||||
|
||||
return self._send_hm_to_handler(context.session, db_hm)
|
||||
|
||||
@wsme_pecan.wsexpose(hm_types.HealthMonitorRootResponse, wtypes.text,
|
||||
body=hm_types.HealthMonitorRootPUT, status_code=200)
|
||||
def put(self, id, health_monitor_):
|
||||
"""Updates a health monitor."""
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
health_monitor = health_monitor_.healthmonitor
|
||||
db_hm = self._get_db_hm(context.session, id)
|
||||
self._test_lb_and_listener_and_pool_statuses(context.session, db_hm)
|
||||
|
||||
self.repositories.health_monitor.update(
|
||||
context.session, db_hm.id,
|
||||
provisioning_status=constants.PENDING_UPDATE)
|
||||
|
||||
try:
|
||||
LOG.info(_LI("Sending Update of Health Monitor for Pool %s to "
|
||||
"handler"), id)
|
||||
self.handler.update(db_hm, health_monitor)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(
|
||||
reraise=False), db_api.get_lock_session() as lock_session:
|
||||
self._reset_lb_listener_pool_statuses(
|
||||
lock_session, db_hm)
|
||||
# Health Monitor now goes to ERROR
|
||||
self.repositories.health_monitor.update(
|
||||
lock_session, db_hm.id,
|
||||
provisioning_status=constants.ERROR)
|
||||
db_hm = self._get_db_hm(context.session, id)
|
||||
result = self._convert_db_to_type(
|
||||
db_hm, hm_types.HealthMonitorResponse)
|
||||
return hm_types.HealthMonitorRootResponse(healthmonitor=result)
|
||||
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
|
||||
def delete(self, id):
|
||||
"""Deletes a health monitor."""
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
db_hm = self._get_db_hm(context.session, id)
|
||||
self._test_lb_and_listener_and_pool_statuses(context.session, db_hm)
|
||||
|
||||
self.repositories.health_monitor.update(
|
||||
context.session, db_hm.id,
|
||||
provisioning_status=constants.PENDING_DELETE)
|
||||
|
||||
try:
|
||||
LOG.info(_LI("Sending Deletion of Health Monitor for Pool %s to "
|
||||
"handler"), id)
|
||||
self.handler.delete(db_hm)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(
|
||||
reraise=False), db_api.get_lock_session() as lock_session:
|
||||
self._reset_lb_listener_pool_statuses(
|
||||
lock_session, db_hm)
|
||||
# Health Monitor now goes to ERROR
|
||||
self.repositories.health_monitor.update(
|
||||
lock_session, db_hm.id,
|
||||
provisioning_status=constants.ERROR)
|
||||
db_hm = self.repositories.health_monitor.get(
|
||||
context.session, id=id)
|
||||
result = self._convert_db_to_type(
|
||||
db_hm, hm_types.HealthMonitorResponse)
|
||||
return hm_types.HealthMonitorRootResponse(healthmonitor=result)
|
|
@ -0,0 +1,129 @@
|
|||
# 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.
|
||||
|
||||
from wsme import types as wtypes
|
||||
|
||||
from octavia.api.common import types
|
||||
from octavia.common import constants
|
||||
|
||||
|
||||
class BaseHealthMonitorType(types.BaseType):
|
||||
_type_to_model_map = {'admin_state_up': 'enabled',
|
||||
'max_retries': 'rise_threshold',
|
||||
'max_retries_down': 'fall_threshold'}
|
||||
|
||||
|
||||
class MinimalPool(types.BaseType):
|
||||
id = wtypes.wsattr(wtypes.UuidType())
|
||||
|
||||
|
||||
class HealthMonitorResponse(BaseHealthMonitorType):
|
||||
"""Defines which attributes are to be shown on any response."""
|
||||
id = wtypes.wsattr(wtypes.UuidType())
|
||||
name = wtypes.wsattr(wtypes.StringType())
|
||||
type = wtypes.wsattr(wtypes.text)
|
||||
delay = wtypes.wsattr(wtypes.IntegerType())
|
||||
timeout = wtypes.wsattr(wtypes.IntegerType())
|
||||
max_retries = wtypes.wsattr(wtypes.IntegerType())
|
||||
max_retries_down = wtypes.wsattr(wtypes.IntegerType())
|
||||
http_method = wtypes.wsattr(wtypes.text)
|
||||
url_path = wtypes.wsattr(wtypes.text)
|
||||
expected_codes = wtypes.wsattr(wtypes.text)
|
||||
admin_state_up = wtypes.wsattr(bool)
|
||||
# TODO(johnsom) Remove after deprecation (R series)
|
||||
project_id = wtypes.wsattr(wtypes.StringType())
|
||||
# TODO(johnsom) Remove after deprecation (R series)
|
||||
tenant_id = wtypes.wsattr(wtypes.StringType())
|
||||
pools = wtypes.wsattr([MinimalPool])
|
||||
provisioning_status = wtypes.wsattr(wtypes.StringType())
|
||||
operating_status = wtypes.wsattr(wtypes.StringType())
|
||||
created_at = wtypes.wsattr(wtypes.datetime.datetime)
|
||||
updated_at = wtypes.wsattr(wtypes.datetime.datetime)
|
||||
|
||||
@classmethod
|
||||
def from_data_model(cls, data_model, children=False):
|
||||
healthmonitor = super(HealthMonitorResponse, cls).from_data_model(
|
||||
data_model, children=children)
|
||||
healthmonitor.tenant_id = data_model.project_id
|
||||
healthmonitor.pools = [
|
||||
MinimalPool.from_data_model(data_model.pool)
|
||||
]
|
||||
return healthmonitor
|
||||
|
||||
|
||||
class HealthMonitorRootResponse(types.BaseType):
|
||||
healthmonitor = wtypes.wsattr(HealthMonitorResponse)
|
||||
|
||||
|
||||
class HealthMonitorsRootResponse(types.BaseType):
|
||||
healthmonitors = wtypes.wsattr([HealthMonitorResponse])
|
||||
|
||||
|
||||
class HealthMonitorPOST(BaseHealthMonitorType):
|
||||
"""Defines mandatory and optional attributes of a POST request."""
|
||||
name = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
type = wtypes.wsattr(
|
||||
wtypes.Enum(str, *constants.SUPPORTED_HEALTH_MONITOR_TYPES),
|
||||
mandatory=True)
|
||||
delay = wtypes.wsattr(wtypes.IntegerType(minimum=0), mandatory=True)
|
||||
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)
|
||||
max_retries = wtypes.wsattr(
|
||||
wtypes.IntegerType(minimum=constants.MIN_HM_RETRIES,
|
||||
maximum=constants.MAX_HM_RETRIES),
|
||||
mandatory=True)
|
||||
http_method = wtypes.wsattr(
|
||||
wtypes.Enum(str, *constants.SUPPORTED_HEALTH_MONITOR_HTTP_METHODS),
|
||||
default=constants.HEALTH_MONITOR_HTTP_DEFAULT_METHOD)
|
||||
url_path = wtypes.wsattr(
|
||||
types.URLType(require_scheme=False),
|
||||
default=constants.HEALTH_MONITOR_DEFAULT_URL_PATH)
|
||||
expected_codes = wtypes.wsattr(
|
||||
wtypes.StringType(pattern=r'^(\d{3}(\s*,\s*\d{3})*)$|^(\d{3}-\d{3})$'),
|
||||
default=constants.HEALTH_MONITOR_DEFAULT_EXPECTED_CODES)
|
||||
admin_state_up = wtypes.wsattr(bool, default=True)
|
||||
# TODO(johnsom) Remove after deprecation (R series)
|
||||
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
||||
# TODO(johnsom) Remove after deprecation (R series)
|
||||
tenant_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
||||
pool_id = wtypes.wsattr(wtypes.UuidType(), mandatory=True)
|
||||
|
||||
|
||||
class HealthMonitorRootPOST(types.BaseType):
|
||||
healthmonitor = wtypes.wsattr(HealthMonitorPOST)
|
||||
|
||||
|
||||
class HealthMonitorPUT(BaseHealthMonitorType):
|
||||
"""Defines attributes that are acceptable of a PUT request."""
|
||||
name = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
delay = wtypes.wsattr(wtypes.IntegerType(minimum=0))
|
||||
timeout = wtypes.wsattr(wtypes.IntegerType(minimum=0))
|
||||
max_retries_down = wtypes.wsattr(
|
||||
wtypes.IntegerType(minimum=constants.MIN_HM_RETRIES,
|
||||
maximum=constants.MAX_HM_RETRIES))
|
||||
max_retries = wtypes.wsattr(
|
||||
wtypes.IntegerType(minimum=constants.MIN_HM_RETRIES,
|
||||
maximum=constants.MAX_HM_RETRIES))
|
||||
http_method = wtypes.wsattr(
|
||||
wtypes.Enum(str, *constants.SUPPORTED_HEALTH_MONITOR_HTTP_METHODS))
|
||||
url_path = wtypes.wsattr(types.URLType())
|
||||
expected_codes = wtypes.wsattr(
|
||||
wtypes.StringType(pattern=r'^(\d{3}(\s*,\s*\d{3})*)$|^(\d{3}-\d{3})$'))
|
||||
admin_state_up = wtypes.wsattr(bool)
|
||||
|
||||
|
||||
class HealthMonitorRootPUT(types.BaseType):
|
||||
healthmonitor = wtypes.wsattr(HealthMonitorPUT)
|
|
@ -32,7 +32,22 @@ HEALTH_MONITOR_HTTP = 'HTTP'
|
|||
HEALTH_MONITOR_HTTPS = 'HTTPS'
|
||||
SUPPORTED_HEALTH_MONITOR_TYPES = (HEALTH_MONITOR_HTTP, HEALTH_MONITOR_HTTPS,
|
||||
HEALTH_MONITOR_PING, HEALTH_MONITOR_TCP)
|
||||
HEALTH_MONITOR_HTTP_DEFAULT_METHOD = 'GET'
|
||||
HEALTH_MONITOR_HTTP_METHOD_GET = 'GET'
|
||||
HEALTH_MONITOR_HTTP_METHOD_HEAD = 'HEAD'
|
||||
HEALTH_MONITOR_HTTP_METHOD_POST = 'POST'
|
||||
HEALTH_MONITOR_HTTP_METHOD_PUT = 'PUT'
|
||||
HEALTH_MONITOR_HTTP_METHOD_DELETE = 'DELETE'
|
||||
HEALTH_MONITOR_HTTP_METHOD_TRACE = 'TRACE'
|
||||
HEALTH_MONITOR_HTTP_METHOD_OPTIONS = 'OPTIONS'
|
||||
HEALTH_MONITOR_HTTP_METHOD_CONNECT = 'CONNECT'
|
||||
HEALTH_MONITOR_HTTP_METHOD_PATCH = 'PATCH'
|
||||
HEALTH_MONITOR_HTTP_DEFAULT_METHOD = HEALTH_MONITOR_HTTP_METHOD_GET
|
||||
SUPPORTED_HEALTH_MONITOR_HTTP_METHODS = (
|
||||
HEALTH_MONITOR_HTTP_METHOD_GET, HEALTH_MONITOR_HTTP_METHOD_HEAD,
|
||||
HEALTH_MONITOR_HTTP_METHOD_POST, HEALTH_MONITOR_HTTP_METHOD_PUT,
|
||||
HEALTH_MONITOR_HTTP_METHOD_DELETE, HEALTH_MONITOR_HTTP_METHOD_TRACE,
|
||||
HEALTH_MONITOR_HTTP_METHOD_OPTIONS, HEALTH_MONITOR_HTTP_METHOD_CONNECT,
|
||||
HEALTH_MONITOR_HTTP_METHOD_PATCH)
|
||||
HEALTH_MONITOR_DEFAULT_EXPECTED_CODES = '200'
|
||||
HEALTH_MONITOR_DEFAULT_URL_PATH = '/'
|
||||
|
||||
|
@ -55,6 +70,9 @@ MIN_CONNECTION_LIMIT = -1
|
|||
MIN_WEIGHT = 0
|
||||
MAX_WEIGHT = 256
|
||||
|
||||
MIN_HM_RETRIES = 1
|
||||
MAX_HM_RETRIES = 10
|
||||
|
||||
# Note: The database Amphora table has a foreign key constraint against
|
||||
# the provisioning_status table
|
||||
# Amphora has been allocated to a load balancer
|
||||
|
|
|
@ -220,8 +220,9 @@ class HealthMonitor(BaseDataModel):
|
|||
def __init__(self, id=None, project_id=None, pool_id=None, type=None,
|
||||
delay=None, timeout=None, fall_threshold=None,
|
||||
rise_threshold=None, http_method=None, url_path=None,
|
||||
expected_codes=None, enabled=None, pool=None,
|
||||
provisioning_status=None, name=None):
|
||||
expected_codes=None, enabled=None, pool=None, name=None,
|
||||
provisioning_status=None, operating_status=None,
|
||||
created_at=None, updated_at=None):
|
||||
self.id = id
|
||||
self.project_id = project_id
|
||||
self.pool_id = pool_id
|
||||
|
@ -236,7 +237,10 @@ class HealthMonitor(BaseDataModel):
|
|||
self.enabled = enabled
|
||||
self.pool = pool
|
||||
self.provisioning_status = provisioning_status
|
||||
self.operating_status = operating_status
|
||||
self.name = name
|
||||
self.created_at = created_at
|
||||
self.updated_at = updated_at
|
||||
|
||||
def delete(self):
|
||||
self.pool.health_monitor = None
|
||||
|
|
|
@ -28,14 +28,15 @@ from octavia.common import exceptions
|
|||
from octavia.common import utils
|
||||
|
||||
|
||||
def url(url):
|
||||
def url(url, require_scheme=True):
|
||||
"""Raises an error if the url doesn't look like a URL."""
|
||||
try:
|
||||
if not rfc3986.is_valid_uri(url, require_scheme=True):
|
||||
if not rfc3986.is_valid_uri(url, require_scheme=require_scheme):
|
||||
raise exceptions.InvalidURL(url=url)
|
||||
p_url = rfc3986.urlparse(rfc3986.normalize_uri(url))
|
||||
if p_url.scheme != 'http' and p_url.scheme != 'https':
|
||||
raise exceptions.InvalidURL(url=url)
|
||||
if require_scheme:
|
||||
if p_url.scheme != 'http' and p_url.scheme != 'https':
|
||||
raise exceptions.InvalidURL(url=url)
|
||||
except Exception:
|
||||
raise exceptions.InvalidURL(url=url)
|
||||
return True
|
||||
|
|
|
@ -1599,9 +1599,13 @@ class MarkHealthMonitorActiveInDB(BaseDatabaseTask):
|
|||
|
||||
LOG.debug("Mark ACTIVE in DB for health monitor id: %s",
|
||||
health_mon.pool_id)
|
||||
|
||||
op_status = (constants.ONLINE if health_mon.enabled
|
||||
else constants.OFFLINE)
|
||||
self.health_mon_repo.update(db_apis.get_session(),
|
||||
health_mon.pool_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
provisioning_status=constants.ACTIVE,
|
||||
operating_status=op_status)
|
||||
|
||||
def revert(self, health_mon, *args, **kwargs):
|
||||
"""Mark the health monitor as broken
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
# 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.
|
||||
|
||||
"""add timestamps and operating_status to healthmonitor
|
||||
|
||||
Revision ID: 52377704420e
|
||||
Revises: d85ca7258d21
|
||||
Create Date: 2017-04-13 08:58:18.078170
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from octavia.common import constants
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '52377704420e'
|
||||
down_revision = 'd85ca7258d21'
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column(
|
||||
u'health_monitor',
|
||||
sa.Column(u'created_at', sa.DateTime(), nullable=True)
|
||||
)
|
||||
op.add_column(
|
||||
u'health_monitor',
|
||||
sa.Column(u'updated_at', sa.DateTime(), nullable=True)
|
||||
)
|
||||
|
||||
op.add_column(u'health_monitor',
|
||||
sa.Column(u'operating_status',
|
||||
sa.String(16),
|
||||
nullable=False,
|
||||
server_default=constants.ONLINE)
|
||||
)
|
||||
op.alter_column(u'health_monitor', u'operating_status',
|
||||
existing_type=sa.String(16), server_default=None)
|
||||
|
||||
op.create_foreign_key(
|
||||
u'fk_health_monitor_operating_status_name', u'health_monitor',
|
||||
u'operating_status', [u'operating_status'], [u'name']
|
||||
)
|
|
@ -194,7 +194,7 @@ class Member(base_models.BASE, base_models.IdMixin, base_models.ProjectMixin,
|
|||
|
||||
|
||||
class HealthMonitor(base_models.BASE, base_models.IdMixin,
|
||||
base_models.ProjectMixin,
|
||||
base_models.ProjectMixin, models.TimestampMixin,
|
||||
base_models.NameMixin):
|
||||
|
||||
__data_model__ = data_models.HealthMonitor
|
||||
|
@ -226,7 +226,12 @@ class HealthMonitor(base_models.BASE, base_models.IdMixin,
|
|||
sa.String(16),
|
||||
sa.ForeignKey("provisioning_status.name",
|
||||
name="fk_health_monitor_provisioning_status_name"),
|
||||
nullable=True)
|
||||
nullable=False)
|
||||
operating_status = sa.Column(
|
||||
sa.String(16),
|
||||
sa.ForeignKey("operating_status.name",
|
||||
name="fk_health_monitor_operating_status_name"),
|
||||
nullable=False)
|
||||
|
||||
|
||||
class Pool(base_models.BASE, base_models.IdMixin, base_models.ProjectMixin,
|
||||
|
|
|
@ -169,7 +169,13 @@ def create_member(member_dict, pool_id, has_health_monitor=False):
|
|||
return member_dict
|
||||
|
||||
|
||||
def create_health_monitor(hm_dict, pool_id):
|
||||
hm_dict['id'] = pool_id
|
||||
hm_dict['pool_id'] = pool_id
|
||||
def create_health_monitor(hm_dict, pool_id=None):
|
||||
hm_dict['provisioning_status'] = constants.PENDING_CREATE
|
||||
hm_dict['operating_status'] = constants.OFFLINE
|
||||
if pool_id:
|
||||
hm_dict['id'] = pool_id
|
||||
hm_dict['pool_id'] = pool_id
|
||||
else:
|
||||
if not hm_dict.get('id'):
|
||||
hm_dict['id'] = uuidutils.generate_uuid()
|
||||
return hm_dict
|
||||
|
|
|
@ -72,6 +72,7 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
|||
self.member_repo = repositories.MemberRepository()
|
||||
self.l7policy_repo = repositories.L7PolicyRepository()
|
||||
self.l7rule_repo = repositories.L7RuleRepository()
|
||||
self.health_monitor_repo = repositories.HealthMonitorRepository()
|
||||
self.amphora_repo = repositories.AmphoraRepository()
|
||||
patcher = mock.patch('octavia.api.handlers.controller_simulator.'
|
||||
'handler.SimulatedControllerHandler')
|
||||
|
@ -212,35 +213,20 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
|||
response = self.post(path, body, **status)
|
||||
return response.json
|
||||
|
||||
# TODO(sindhu): Will be modified later in the Health_Monitor review
|
||||
def create_health_monitor(self, lb_id, type, delay, timeout,
|
||||
fall_threshold, rise_threshold, root_tag=None,
|
||||
**optionals):
|
||||
req_dict = {'load_balancer_id': lb_id, 'type': type,
|
||||
def create_health_monitor(self, pool_id, type, delay, timeout,
|
||||
max_retries_down, max_retries,
|
||||
status=None, **optionals):
|
||||
req_dict = {'pool_id': pool_id,
|
||||
'type': type,
|
||||
'delay': delay,
|
||||
'timeout': timeout,
|
||||
'fall_threshold': fall_threshold,
|
||||
'rise_threshold': rise_threshold}
|
||||
'max_retries_down': max_retries_down,
|
||||
'max_retries': max_retries}
|
||||
req_dict.update(optionals)
|
||||
body = {root_tag: req_dict}
|
||||
body = {'healthmonitor': req_dict}
|
||||
path = self.HMS_PATH
|
||||
response = self.post(path, body)
|
||||
return response.json
|
||||
|
||||
# TODO(sindhu): Will be modified according to the
|
||||
# health_monitor_test_cases later
|
||||
def create_health_monitor_with_listener(
|
||||
self, lb_id, listener_id, pool_id, type,
|
||||
delay, timeout, fall_threshold, rise_threshold, **optionals):
|
||||
req_dict = {'type': type,
|
||||
'delay': delay,
|
||||
'timeout': timeout,
|
||||
'fall_threshold': fall_threshold,
|
||||
'rise_threshold': rise_threshold}
|
||||
req_dict.update(optionals)
|
||||
path = self.DEPRECATED_HM_PATH.format(
|
||||
lb_id=lb_id, listener_id=listener_id, pool_id=pool_id)
|
||||
response = self.post(path, req_dict)
|
||||
status = {'status': status} if status else {}
|
||||
response = self.post(path, body, **status)
|
||||
return response.json
|
||||
|
||||
def create_l7policy(self, listener_id, action, **optionals):
|
||||
|
@ -318,6 +304,16 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
|||
self.member_repo.update(db_api.get_session(), member.id,
|
||||
provisioning_status=member_prov,
|
||||
operating_status=op_status)
|
||||
if pool.health_monitor:
|
||||
if autodetect and (pool.health_monitor.provisioning_status ==
|
||||
constants.PENDING_DELETE):
|
||||
hm_prov = constants.DELETED
|
||||
else:
|
||||
hm_prov = prov_status
|
||||
self.health_monitor_repo.update(db_api.get_session(),
|
||||
pool.health_monitor.id,
|
||||
provisioning_status=hm_prov,
|
||||
operating_status=op_status)
|
||||
|
||||
def set_lb_status(self, lb_id, status=None):
|
||||
explicit_status = True if status is not None else False
|
||||
|
@ -409,20 +405,32 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
|||
self.assertEqual(operating_status,
|
||||
api_l7rule.get('operating_status'))
|
||||
|
||||
def assert_correct_hm_status(self, provisioning_status,
|
||||
operating_status, hm_id):
|
||||
api_hm = self.get(self.HM_PATH.format(
|
||||
healthmonitor_id=hm_id)).json.get('healthmonitor')
|
||||
self.assertEqual(provisioning_status,
|
||||
api_hm.get('provisioning_status'))
|
||||
self.assertEqual(operating_status,
|
||||
api_hm.get('operating_status'))
|
||||
|
||||
def assert_correct_status(self, lb_id=None, listener_id=None, pool_id=None,
|
||||
member_id=None, l7policy_id=None, l7rule_id=None,
|
||||
hm_id=None,
|
||||
lb_prov_status=constants.ACTIVE,
|
||||
listener_prov_status=constants.ACTIVE,
|
||||
pool_prov_status=constants.ACTIVE,
|
||||
member_prov_status=constants.ACTIVE,
|
||||
l7policy_prov_status=constants.ACTIVE,
|
||||
l7rule_prov_status=constants.ACTIVE,
|
||||
hm_prov_status=constants.ACTIVE,
|
||||
lb_op_status=constants.ONLINE,
|
||||
listener_op_status=constants.ONLINE,
|
||||
pool_op_status=constants.ONLINE,
|
||||
member_op_status=constants.ONLINE,
|
||||
l7policy_op_status=constants.ONLINE,
|
||||
l7rule_op_status=constants.ONLINE):
|
||||
l7rule_op_status=constants.ONLINE,
|
||||
hm_op_status=constants.ONLINE):
|
||||
if lb_id:
|
||||
self.assert_correct_lb_status(lb_prov_status, lb_op_status, lb_id)
|
||||
if listener_id:
|
||||
|
@ -440,3 +448,6 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
|||
if l7rule_id:
|
||||
self.assert_correct_l7rule_status(
|
||||
l7rule_prov_status, l7rule_op_status, l7policy_id, l7rule_id)
|
||||
if hm_id:
|
||||
self.assert_correct_hm_status(
|
||||
hm_prov_status, hm_op_status, hm_id)
|
||||
|
|
|
@ -0,0 +1,318 @@
|
|||
# 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.
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from octavia.common import constants
|
||||
from octavia.tests.functional.api.v2 import base
|
||||
|
||||
|
||||
class TestHealthMonitor(base.BaseAPITest):
|
||||
|
||||
root_tag = 'healthmonitor'
|
||||
root_tag_list = 'healthmonitors'
|
||||
root_tag_links = 'healthmonitors_links'
|
||||
|
||||
def setUp(self):
|
||||
super(TestHealthMonitor, self).setUp()
|
||||
self.lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get('loadbalancer')
|
||||
self.lb_id = self.lb.get('id')
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.listener = self.create_listener(
|
||||
constants.PROTOCOL_HTTP, 80,
|
||||
self.lb_id).get('listener')
|
||||
self.listener_id = self.listener.get('id')
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.pool = self.create_pool(self.lb_id, constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN)
|
||||
self.pool_id = self.pool.get('pool').get('id')
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.pool_with_listener = self.create_pool(
|
||||
self.lb_id, constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN, listener_id=self.listener_id)
|
||||
self.pool_with_listener_id = (
|
||||
self.pool_with_listener.get('pool').get('id'))
|
||||
self.set_lb_status(self.lb_id)
|
||||
|
||||
def test_get(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_id, constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1).get(self.root_tag)
|
||||
# Set status to ACTIVE/ONLINE because set_lb_status did it in the db
|
||||
api_hm['provisioning_status'] = constants.ACTIVE
|
||||
api_hm['operating_status'] = constants.ONLINE
|
||||
api_hm.pop('updated_at')
|
||||
self.set_lb_status(self.lb_id)
|
||||
response = self.get(self.HM_PATH.format(
|
||||
healthmonitor_id=api_hm.get('id'))).json.get(self.root_tag)
|
||||
response.pop('updated_at')
|
||||
self.assertEqual(api_hm, response)
|
||||
|
||||
def test_bad_get(self):
|
||||
self.get(self.HM_PATH.format(
|
||||
healthmonitor_id=uuidutils.generate_uuid()), status=404)
|
||||
|
||||
def test_get_all(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_id, constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
hms = self.get(self.HMS_PATH).json.get(self.root_tag_list)
|
||||
self.assertIsInstance(hms, list)
|
||||
self.assertEqual(1, len(hms))
|
||||
self.assertEqual(api_hm.get('id'), hms[0].get('id'))
|
||||
|
||||
def test_create_http_monitor_with_relative_path(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_id, constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1, url_path="/").get(self.root_tag)
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=self.pool_id, hm_id=api_hm.get('id'),
|
||||
lb_prov_status=constants.PENDING_UPDATE,
|
||||
listener_prov_status=constants.ACTIVE,
|
||||
pool_prov_status=constants.PENDING_UPDATE,
|
||||
hm_prov_status=constants.PENDING_CREATE,
|
||||
hm_op_status=constants.OFFLINE)
|
||||
|
||||
def test_create_sans_listener(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_id, constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1).get(self.root_tag)
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=self.pool_id, hm_id=api_hm.get('id'),
|
||||
lb_prov_status=constants.PENDING_UPDATE,
|
||||
listener_prov_status=constants.ACTIVE,
|
||||
pool_prov_status=constants.PENDING_UPDATE,
|
||||
hm_prov_status=constants.PENDING_CREATE,
|
||||
hm_op_status=constants.OFFLINE)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.assertEqual(constants.HEALTH_MONITOR_HTTP, api_hm.get('type'))
|
||||
self.assertEqual(1, api_hm.get('delay'))
|
||||
self.assertEqual(1, api_hm.get('timeout'))
|
||||
self.assertEqual(1, api_hm.get('max_retries_down'))
|
||||
self.assertEqual(1, api_hm.get('max_retries'))
|
||||
# Verify optional field defaults
|
||||
self.assertEqual('GET', api_hm.get('http_method'))
|
||||
self.assertEqual('/', api_hm.get('url_path'))
|
||||
self.assertEqual('200', api_hm.get('expected_codes'))
|
||||
|
||||
def test_create_with_listener(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_with_listener_id, constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1).get(self.root_tag)
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=self.pool_with_listener_id, hm_id=api_hm.get('id'),
|
||||
lb_prov_status=constants.PENDING_UPDATE,
|
||||
listener_prov_status=constants.PENDING_UPDATE,
|
||||
pool_prov_status=constants.PENDING_UPDATE,
|
||||
hm_prov_status=constants.PENDING_CREATE,
|
||||
hm_op_status=constants.OFFLINE)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.assertEqual(constants.HEALTH_MONITOR_HTTP, api_hm.get('type'))
|
||||
self.assertEqual(1, api_hm.get('delay'))
|
||||
self.assertEqual(1, api_hm.get('timeout'))
|
||||
self.assertEqual(1, api_hm.get('max_retries_down'))
|
||||
self.assertEqual(1, api_hm.get('max_retries'))
|
||||
# Verify optional field defaults
|
||||
self.assertEqual('GET', api_hm.get('http_method'))
|
||||
self.assertEqual('/', api_hm.get('url_path'))
|
||||
self.assertEqual('200', api_hm.get('expected_codes'))
|
||||
|
||||
# TODO(rm_work) Remove after deprecation of project_id in POST (R series)
|
||||
def test_create_with_project_id_is_ignored(self):
|
||||
pid = uuidutils.generate_uuid()
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_id, constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1,
|
||||
project_id=pid).get(self.root_tag)
|
||||
self.assertEqual(self.project_id, api_hm.get('project_id'))
|
||||
|
||||
def test_bad_create(self):
|
||||
hm_json = {'name': 'test1', 'pool_id': self.pool_id}
|
||||
self.post(self.HMS_PATH, self._build_body(hm_json), status=400)
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=self.pool_id)
|
||||
|
||||
def test_create_with_bad_handler(self):
|
||||
self.handler_mock().health_monitor.create.side_effect = Exception()
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_with_listener_id,
|
||||
constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1).get(self.root_tag)
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=self.pool_with_listener_id,
|
||||
hm_id=api_hm.get('id'),
|
||||
lb_prov_status=constants.ACTIVE,
|
||||
listener_prov_status=constants.ACTIVE,
|
||||
pool_prov_status=constants.ACTIVE,
|
||||
hm_prov_status=constants.ERROR,
|
||||
hm_op_status=constants.OFFLINE)
|
||||
|
||||
def test_duplicate_create(self):
|
||||
# TODO(rm_work): I am fairly certain this is the same issue as we see
|
||||
# in test_repositories.py where PySqlite commits too early and can't
|
||||
# roll back, causing things to get out of whack. This runs fine solo.
|
||||
# It would be useful to test this *in reality* and see if it breaks.
|
||||
self.skipTest("PySqlite transaction handling is broken. We can unskip"
|
||||
"this when `test_sqlite_transactions_broken` fails.")
|
||||
self.create_health_monitor(
|
||||
self.pool_id, constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.create_health_monitor(
|
||||
self.pool_id, constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1,
|
||||
status=409)
|
||||
|
||||
def test_update(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_with_listener_id,
|
||||
constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
new_hm = {'max_retries': 2}
|
||||
self.put(
|
||||
self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
|
||||
self._build_body(new_hm))
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=self.pool_with_listener_id, hm_id=api_hm.get('id'),
|
||||
lb_prov_status=constants.PENDING_UPDATE,
|
||||
listener_prov_status=constants.PENDING_UPDATE,
|
||||
pool_prov_status=constants.PENDING_UPDATE,
|
||||
hm_prov_status=constants.PENDING_UPDATE)
|
||||
|
||||
def test_bad_update(self):
|
||||
self.skip("This test will need reviewed after a validation layer is "
|
||||
"built")
|
||||
self.create_health_monitor(self.lb_id,
|
||||
self.pool_id,
|
||||
constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1)
|
||||
new_hm = {'type': 'bad_type', 'delay': 2}
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.put(self.HM_PATH, self._build_body(new_hm), status=400)
|
||||
|
||||
def test_update_with_bad_handler(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_with_listener_id,
|
||||
constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
new_hm = {'max_retries': 2}
|
||||
self.handler_mock().health_monitor.update.side_effect = Exception()
|
||||
self.put(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
|
||||
self._build_body(new_hm))
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=self.pool_with_listener_id, hm_id=api_hm.get('id'),
|
||||
hm_prov_status=constants.ERROR)
|
||||
|
||||
def test_delete(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_with_listener_id,
|
||||
constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
hm = self.get(self.HM_PATH.format(
|
||||
healthmonitor_id=api_hm.get('id'))).json.get(self.root_tag)
|
||||
api_hm['provisioning_status'] = constants.ACTIVE
|
||||
api_hm['operating_status'] = constants.ONLINE
|
||||
self.assertIsNone(api_hm.pop('updated_at'))
|
||||
self.assertIsNotNone(hm.pop('updated_at'))
|
||||
self.assertEqual(api_hm, hm)
|
||||
self.delete(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')))
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=self.pool_with_listener_id, hm_id=api_hm.get('id'),
|
||||
lb_prov_status=constants.PENDING_UPDATE,
|
||||
listener_prov_status=constants.PENDING_UPDATE,
|
||||
pool_prov_status=constants.PENDING_UPDATE,
|
||||
hm_prov_status=constants.PENDING_DELETE)
|
||||
|
||||
def test_bad_delete(self):
|
||||
self.delete(
|
||||
self.HM_PATH.format(healthmonitor_id=uuidutils.generate_uuid()),
|
||||
status=404)
|
||||
|
||||
def test_delete_with_bad_handler(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_with_listener_id,
|
||||
constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
hm = self.get(self.HM_PATH.format(
|
||||
healthmonitor_id=api_hm.get('id'))).json.get(self.root_tag)
|
||||
api_hm['provisioning_status'] = constants.ACTIVE
|
||||
api_hm['operating_status'] = constants.ONLINE
|
||||
self.assertIsNone(api_hm.pop('updated_at'))
|
||||
self.assertIsNotNone(hm.pop('updated_at'))
|
||||
self.assertEqual(api_hm, hm)
|
||||
self.handler_mock().health_monitor.delete.side_effect = Exception()
|
||||
self.delete(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')))
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=self.pool_with_listener_id, hm_id=api_hm.get('id'),
|
||||
hm_prov_status=constants.ERROR)
|
||||
|
||||
def test_create_when_lb_pending_update(self):
|
||||
self.put(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
body={'loadbalancer': {'name': 'test_name_change'}})
|
||||
self.create_health_monitor(
|
||||
self.pool_with_listener_id,
|
||||
constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1, status=409)
|
||||
|
||||
def test_update_when_lb_pending_update(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_id, constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.put(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
body={'loadbalancer': {'name': 'test_name_change'}})
|
||||
new_hm = {'max_retries': 2}
|
||||
self.put(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
|
||||
body=self._build_body(new_hm), status=409)
|
||||
|
||||
def test_delete_when_lb_pending_update(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_id, constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.put(self.LB_PATH.format(lb_id=self.lb_id),
|
||||
body={'loadbalancer': {'name': 'test_name_change'}})
|
||||
self.delete(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
|
||||
status=409)
|
||||
|
||||
def test_create_when_lb_pending_delete(self):
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.create_health_monitor(
|
||||
self.pool_id,
|
||||
constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1, status=409)
|
||||
|
||||
def test_update_when_lb_pending_delete(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_id, constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
new_hm = {'max_retries': 2}
|
||||
self.put(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
|
||||
body=self._build_body(new_hm), status=409)
|
||||
|
||||
def test_delete_when_lb_pending_delete(self):
|
||||
api_hm = self.create_health_monitor(
|
||||
self.pool_id, constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.delete(self.LB_PATH.format(lb_id=self.lb_id))
|
||||
self.delete(self.HM_PATH.format(healthmonitor_id=api_hm.get('id')),
|
||||
status=409)
|
|
@ -89,6 +89,8 @@ class ModelTestMixin(object):
|
|||
'fall_threshold': 1,
|
||||
'rise_threshold': 1,
|
||||
'enabled': True,
|
||||
'operating_status': constants.ONLINE,
|
||||
'provisioning_status': constants.ACTIVE,
|
||||
'project_id': self.FAKE_UUID_1}
|
||||
kwargs.update(overrides)
|
||||
return self._insert(session, models.HealthMonitor, kwargs)
|
||||
|
|
|
@ -340,7 +340,9 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
|||
'id': uuidutils.generate_uuid()}
|
||||
health_monitor = {'type': constants.HEALTH_MONITOR_HTTP, 'delay': 1,
|
||||
'timeout': 1, 'fall_threshold': 1,
|
||||
'rise_threshold': 1, 'enabled': True}
|
||||
'rise_threshold': 1, 'enabled': True,
|
||||
'operating_status': constants.OFFLINE,
|
||||
'provisioning_status': constants.PENDING_CREATE}
|
||||
sp = {'type': constants.SESSION_PERSISTENCE_APP_COOKIE,
|
||||
'cookie_name': 'cookie_name'}
|
||||
pool = {'protocol': constants.PROTOCOL_HTTP, 'name': 'pool1',
|
||||
|
@ -362,7 +364,9 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
|||
'enabled': True}
|
||||
r_health_monitor = {'type': constants.HEALTH_MONITOR_HTTP, 'delay': 1,
|
||||
'timeout': 1, 'fall_threshold': 1,
|
||||
'rise_threshold': 1, 'enabled': True}
|
||||
'rise_threshold': 1, 'enabled': True,
|
||||
'operating_status': constants.OFFLINE,
|
||||
'provisioning_status': constants.PENDING_CREATE}
|
||||
redirect_pool = {'protocol': constants.PROTOCOL_HTTP, 'name': 'pool1',
|
||||
'description': 'desc1', 'project_id': project_id,
|
||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
|
@ -1311,6 +1315,7 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
|||
name="health_mon1", type=constants.HEALTH_MONITOR_HTTP,
|
||||
delay=1, timeout=1, fall_threshold=1, rise_threshold=1,
|
||||
provisioning_status=constants.ACTIVE,
|
||||
operating_status=constants.ONLINE,
|
||||
enabled=True, pool_id=pool.id)
|
||||
self.assertTrue(self.repos.check_quota_met(self.session,
|
||||
self.session,
|
||||
|
@ -1340,6 +1345,7 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
|
|||
name="health_mon1", type=constants.HEALTH_MONITOR_HTTP,
|
||||
delay=1, timeout=1, fall_threshold=1, rise_threshold=1,
|
||||
provisioning_status=constants.DELETED,
|
||||
operating_status=constants.OFFLINE,
|
||||
enabled=True, pool_id=pool.id)
|
||||
self.assertFalse(self.repos.check_quota_met(self.session,
|
||||
self.session,
|
||||
|
@ -1894,7 +1900,9 @@ class PoolRepositoryTest(BaseRepositoryTest):
|
|||
hm = self.hm_repo.create(self.session, pool_id=pool.id,
|
||||
type=constants.HEALTH_MONITOR_HTTP,
|
||||
delay=1, timeout=1, fall_threshold=1,
|
||||
rise_threshold=1, enabled=True)
|
||||
rise_threshold=1, enabled=True,
|
||||
provisioning_status=constants.ACTIVE,
|
||||
operating_status=constants.ONLINE)
|
||||
new_pool = self.pool_repo.get(self.session, id=pool.id)
|
||||
self.assertEqual(pool, new_pool)
|
||||
self.assertEqual(hm, new_pool.health_monitor)
|
||||
|
@ -1922,7 +1930,9 @@ class PoolRepositoryTest(BaseRepositoryTest):
|
|||
hm = self.hm_repo.create(self.session, pool_id=pool.id,
|
||||
type=constants.HEALTH_MONITOR_HTTP,
|
||||
delay=1, timeout=1, fall_threshold=1,
|
||||
rise_threshold=1, enabled=True)
|
||||
rise_threshold=1, enabled=True,
|
||||
provisioning_status=constants.ACTIVE,
|
||||
operating_status=constants.ONLINE)
|
||||
member = self.member_repo.create(self.session, id=self.FAKE_UUID_3,
|
||||
project_id=self.FAKE_UUID_2,
|
||||
pool_id=pool.id,
|
||||
|
@ -2459,6 +2469,8 @@ class HealthMonitorRepositoryTest(BaseRepositoryTest):
|
|||
pool_id=pool_id, delay=1, timeout=1, fall_threshold=1,
|
||||
rise_threshold=1, http_method="POST",
|
||||
url_path="http://localhost:80/index.php",
|
||||
provisioning_status=constants.ACTIVE,
|
||||
operating_status=constants.ONLINE,
|
||||
expected_codes="200", enabled=True)
|
||||
return health_monitor
|
||||
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
# 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.
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
from wsme import exc
|
||||
from wsme.rest import json as wsme_json
|
||||
from wsme import types as wsme_types
|
||||
|
||||
from octavia.api.v2.types import health_monitor as hm_type
|
||||
from octavia.common import constants
|
||||
from octavia.tests.unit.api.v2.types import base
|
||||
|
||||
|
||||
class TestHealthMonitor(object):
|
||||
|
||||
_type = None
|
||||
|
||||
def test_invalid_type(self):
|
||||
body = {"delay": 1, "timeout": 1, "max_retries": 1}
|
||||
if self._type is hm_type.HealthMonitorPOST:
|
||||
body.update({"type": 1, "pool_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_delay(self):
|
||||
body = {"delay": "one", "timeout": 1, "max_retries": 1}
|
||||
if self._type is hm_type.HealthMonitorPOST:
|
||||
body.update({"type": constants.PROTOCOL_HTTP,
|
||||
"pool_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
def test_invalid_timeout(self):
|
||||
body = {"delay": 1, "timeout": "one", "max_retries": 1}
|
||||
if self._type is hm_type.HealthMonitorPOST:
|
||||
body.update({"type": constants.PROTOCOL_HTTP,
|
||||
"pool_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
def test_invalid_max_retries_down(self):
|
||||
body = {"delay": 1, "timeout": 1, "max_retries": "one"}
|
||||
if self._type is hm_type.HealthMonitorPOST:
|
||||
body.update({"type": constants.PROTOCOL_HTTP,
|
||||
"pool_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
def test_invalid_max_retries(self):
|
||||
body = {"delay": 1, "timeout": 1, "max_retries": "one"}
|
||||
if self._type is hm_type.HealthMonitorPOST:
|
||||
body.update({"type": constants.PROTOCOL_HTTP,
|
||||
"pool_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(ValueError, wsme_json.fromjson, self._type, body)
|
||||
|
||||
def test_invalid_http_method(self):
|
||||
body = {"delay": 1, "timeout": 1, "max_retries": 1,
|
||||
"http_method": 1}
|
||||
if self._type is hm_type.HealthMonitorPOST:
|
||||
body.update({"type": constants.PROTOCOL_HTTP,
|
||||
"pool_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_url_path(self):
|
||||
body = {"delay": 1, "timeout": 1, "max_retries": 1, "url_path": 1}
|
||||
if self._type is hm_type.HealthMonitorPOST:
|
||||
body.update({"type": constants.PROTOCOL_HTTP,
|
||||
"pool_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_invalid_expected_codes(self):
|
||||
body = {"delay": 1, "timeout": 1, "max_retries": 1,
|
||||
"expected_codes": "lol"}
|
||||
if self._type is hm_type.HealthMonitorPOST:
|
||||
body.update({"type": constants.PROTOCOL_HTTP,
|
||||
"pool_id": uuidutils.generate_uuid()})
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
|
||||
class TestHealthMonitorPOST(base.BaseTypesTest, TestHealthMonitor):
|
||||
|
||||
_type = hm_type.HealthMonitorPOST
|
||||
|
||||
def test_health_monitor(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": 1, "max_retries_down": 1, "max_retries": 1,
|
||||
"pool_id": uuidutils.generate_uuid()}
|
||||
hm = wsme_json.fromjson(self._type, body)
|
||||
self.assertTrue(hm.admin_state_up)
|
||||
|
||||
def test_type_mandatory(self):
|
||||
body = {"delay": 80, "timeout": 1, "max_retries": 1,
|
||||
"pool_id": uuidutils.generate_uuid()}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_delay_mandatory(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "timeout": 1,
|
||||
"max_retries": 1, "pool_id": uuidutils.generate_uuid()}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_timeout_mandatory(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"max_retries": 1, "pool_id": uuidutils.generate_uuid()}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_max_retries_mandatory(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": 1, "pool_id": uuidutils.generate_uuid()}
|
||||
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
|
||||
body)
|
||||
|
||||
def test_default_health_monitor_values(self):
|
||||
# http_method = 'GET'
|
||||
# url_path = '/'
|
||||
# expected_codes = '200'
|
||||
# max_retries_down = 3
|
||||
# admin_state_up = True
|
||||
# The above are not required but should have the above example defaults
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": 1, "max_retries": 1,
|
||||
"pool_id": uuidutils.generate_uuid()}
|
||||
hmpost = wsme_json.fromjson(self._type, body)
|
||||
self.assertEqual('GET', hmpost.http_method)
|
||||
self.assertEqual('/', hmpost.url_path)
|
||||
self.assertEqual('200', hmpost.expected_codes)
|
||||
self.assertEqual(3, hmpost.max_retries_down)
|
||||
self.assertTrue(hmpost.admin_state_up)
|
||||
|
||||
def test_non_uuid_project_id(self):
|
||||
body = {"type": constants.HEALTH_MONITOR_HTTP, "delay": 1,
|
||||
"timeout": 1, "max_retries_down": 1, "max_retries": 1,
|
||||
"project_id": "non-uuid",
|
||||
"pool_id": uuidutils.generate_uuid()}
|
||||
hm = wsme_json.fromjson(self._type, body)
|
||||
self.assertEqual(hm.project_id, body['project_id'])
|
||||
|
||||
|
||||
class TestHealthMonitorPUT(base.BaseTypesTest, TestHealthMonitor):
|
||||
|
||||
_type = hm_type.HealthMonitorPUT
|
||||
|
||||
def test_health_monitor(self):
|
||||
body = {"http_method": constants.HEALTH_MONITOR_HTTP_METHOD_HEAD}
|
||||
hm = wsme_json.fromjson(self._type, body)
|
||||
self.assertEqual(wsme_types.Unset, hm.admin_state_up)
|
|
@ -1713,6 +1713,7 @@ class TestDatabaseTasks(base.TestCase):
|
|||
mock_health_mon_repo_update.assert_called_once_with(
|
||||
'TEST',
|
||||
POOL_ID,
|
||||
operating_status=constants.ONLINE,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
|
||||
# Test the revert
|
||||
|
|
Loading…
Reference in New Issue