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:
Adam Harwell 2017-04-20 04:47:38 +09:00
parent 13730f5bfe
commit 7ea7888b0d
17 changed files with 1016 additions and 46 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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']
)

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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