Add oslo middleware healthcheck to Octavia API

healthcheck middleware adds a /healthcheck url that allows
unauthenticated access to provide a simple check when running
octavia-api behind a load balancer

https://docs.openstack.org/oslo.middleware/latest/reference/healthcheck_plugins.html

Co-authored-by: Michael Johnson <johnsomor@gmail.com>
Change-Id: I10db6226750f7b7c703067d2ab82eea3a9875112
This commit is contained in:
Sam Morrison 2020-03-04 10:35:56 +11:00 committed by Michael Johnson
parent 22fa179edc
commit 18020e6c88
29 changed files with 1121 additions and 118 deletions

View File

@ -0,0 +1,599 @@
..
Copyright 2020 Red Hat, Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
=============================
Octavia API Health Monitoring
=============================
The Octavia API provides a health monitoring endpoint that can be used by
external load balancers to manage the Octavia API pool. When properly
configured, the health monitoring endpoint will reflect the full operational
status of the Octavia API.
The Octavia API health monitoring endpoint extends the `OpenStack Oslo
middleware healthcheck <https://docs.openstack.org/oslo.middleware/latest/reference/healthcheck_plugins.html>`_ library to test the Octavia Pecan API framework and associated services.
Oslo Healthcheck Queries
========================
Oslo middleware healthcheck supports HTTP **"GET"** and **"HEAD"** methods.
The response from Oslo middleware healthcheck can be customized by specifying
the acceptable response type for the request.
Oslo middleware healthcheck currently supports the following types:
* text/plain
* text/html
* application/json
If the requested type is not one of the above, it defaults to text/plain.
.. note::
The content of the response "reasons" will vary based on the backend plugins
enabled in Oslo middleware healthcheck. It is a best practice to only rely
on the HTTP status code for Octavia API health monitoring.
Example Responses
-----------------
Example passing output for text/plain with *detailed* False:
.. code-block:: bash
$ curl -i http://198.51.100.10/load-balancer/healthcheck
HTTP/1.1 200 OK
Date: Mon, 16 Mar 2020 18:10:27 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: text/plain; charset=UTF-8
Content-Length: 2
x-openstack-request-id: req-9c6f4303-63a7-4f30-8afc-39340658702f
Connection: close
Vary: Accept-Encoding
OK
Example failing output for text/plain with *detailed* False:
.. code-block:: bash
$ curl -i http://198.51.100.10/load-balancer/healthcheck
HTTP/1.1 503 Service Unavailable
Date: Mon, 16 Mar 2020 18:42:12 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: text/plain; charset=UTF-8
Content-Length: 36
x-openstack-request-id: req-84024269-2dfb-41ad-bfda-b3e1da138bba
Connection: close
Example passing output for text/html with *detailed* False:
.. code-block:: bash
$ curl -i -H "Accept: text/html" http://198.51.100.10/load-balancer/healthcheck
HTTP/1.1 200 OK
Date: Mon, 16 Mar 2020 18:25:11 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: text/html; charset=UTF-8
Content-Length: 239
x-openstack-request-id: req-b212d619-146f-4b50-91a3-5da16051badc
Connection: close
Vary: Accept-Encoding
<HTML>
<HEAD><TITLE>Healthcheck Status</TITLE></HEAD>
<BODY>
<H2>Result of 1 checks:</H2>
<TABLE bgcolor="#ffffff" border="1">
<TBODY>
<TR>
<TH>
Reason
</TH>
</TR>
<TR>
<TD>OK</TD>
</TR>
</TBODY>
</TABLE>
<HR></HR>
</BODY>
</HTML>
Example failing output for text/html with *detailed* False:
.. code-block:: bash
$ curl -i -H "Accept: text/html" http://198.51.100.10/load-balancer/healthcheck
HTTP/1.1 503 Service Unavailable
Date: Mon, 16 Mar 2020 18:42:22 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: text/html; charset=UTF-8
Content-Length: 273
x-openstack-request-id: req-c91dd214-85ca-4d33-9fa3-2db81566d9e5
Connection: close
<HTML>
<HEAD><TITLE>Healthcheck Status</TITLE></HEAD>
<BODY>
<H2>Result of 1 checks:</H2>
<TABLE bgcolor="#ffffff" border="1">
<TBODY>
<TR>
<TH>
Reason
</TH>
</TR>
<TR>
<TD>The Octavia database is unavailable.</TD>
</TR>
</TBODY>
</TABLE>
<HR></HR>
</BODY>
</HTML>
Example passing output for application/json with *detailed* False:
.. code-block:: bash
$ curl -i -H "Accept: application/json" http://192.51.100.10/load-balancer/healthcheck
HTTP/1.1 200 OK
Date: Mon, 16 Mar 2020 18:34:42 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: application/json
Content-Length: 62
x-openstack-request-id: req-417dc85c-e64e-496e-a461-494a3e6a5479
Connection: close
{
"detailed": false,
"reasons": [
"OK"
]
}
Example failing output for application/json with *detailed* False:
.. code-block:: bash
$ curl -i -H "Accept: application/json" http://192.51.100.10/load-balancer/healthcheck
HTTP/1.1 503 Service Unavailable
Date: Mon, 16 Mar 2020 18:46:28 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: application/json
Content-Length: 96
x-openstack-request-id: req-de50b057-6105-4fca-a758-c872ef28bbfa
Connection: close
{
"detailed": false,
"reasons": [
"The Octavia database is unavailable."
]
}
Example Detailed Responses
--------------------------
Example passing output for text/plain with *detailed* True:
.. code-block:: bash
$ curl -i http://198.51.100.10/load-balancer/healthcheck
HTTP/1.1 200 OK
Date: Mon, 16 Mar 2020 18:10:27 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: text/plain; charset=UTF-8
Content-Length: 2
x-openstack-request-id: req-9c6f4303-63a7-4f30-8afc-39340658702f
Connection: close
Vary: Accept-Encoding
OK
Example failing output for text/plain with *detailed* True:
.. code-block:: bash
$ curl -i http://198.51.100.10/load-balancer/healthcheck
HTTP/1.1 503 Service Unavailable
Date: Mon, 16 Mar 2020 23:41:23 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: text/plain; charset=UTF-8
Content-Length: 36
x-openstack-request-id: req-2cd046cb-3a6c-45e3-921d-5f4a9e65c63e
Connection: close
Example passing output for text/html with *detailed* True:
.. code-block:: bash
$ curl -i -H "Accept: text/html" http://198.51.100.10/load-balancer/healthcheck
HTTP/1.1 200 OK
Date: Mon, 16 Mar 2020 22:11:54 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: text/html; charset=UTF-8
Content-Length: 9927
x-openstack-request-id: req-ae7404c9-b183-46dc-bb1b-e5f4e4984a57
Connection: close
Vary: Accept-Encoding
<HTML>
<HEAD><TITLE>Healthcheck Status</TITLE></HEAD>
<BODY>
<H1>Server status</H1>
<B>Server hostname:</B><PRE>devstack2</PRE>
<B>Current time:</B><PRE>2020-03-16 22:11:54.320529</PRE>
<B>Python version:</B><PRE>3.6.9 (default, Nov 7 2019, 10:44:02)
[GCC 8.3.0]</PRE>
<B>Platform:</B><PRE>Linux-4.15.0-88-generic-x86_64-with-Ubuntu-18.04-bionic</PRE>
<HR></HR>
<H2>Garbage collector:</H2>
<B>Counts:</B><PRE>(28, 10, 4)</PRE>
<B>Thresholds:</B><PRE>(700, 10, 10)</PRE>
<HR></HR>
<H2>Result of 1 checks:</H2>
<TABLE bgcolor="#ffffff" border="1">
<TBODY>
<TR>
<TH>
Kind
</TH>
<TH>
Reason
</TH>
<TH>
Details
</TH>
</TR>
<TR>
<TD>OctaviaDBCheckResult</TD>
<TD>OK</TD>
<TD></TD>
</TR>
</TBODY>
</TABLE>
<HR></HR>
<H2>1 greenthread(s) active:</H2>
<TABLE bgcolor="#ffffff" border="1">
<TBODY>
<TR>
<TD><PRE> <...> </PRE></TD>
</TR>
</TBODY>
</TABLE>
<HR></HR>
<H2>1 thread(s) active:</H2>
<TABLE bgcolor="#ffffff" border="1">
<TBODY>
<TR>
<TD><PRE> <...> </PRE></TD>
</TR>
</TBODY>
</TABLE>
</BODY>
</HTML>
Example failing output for text/html with *detailed* True:
.. code-block:: bash
$ curl -i -H "Accept: text/html" http://198.51.100.10/load-balancer/healthcheck
HTTP/1.1 503 Service Unavailable
Date: Mon, 16 Mar 2020 23:43:52 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: text/html; charset=UTF-8
Content-Length: 10211
x-openstack-request-id: req-39b65058-6dc3-4069-a2d5-8a9714dba61d
Connection: close
<HTML>
<HEAD><TITLE>Healthcheck Status</TITLE></HEAD>
<BODY>
<H1>Server status</H1>
<B>Server hostname:</B><PRE>devstack2</PRE>
<B>Current time:</B><PRE>2020-03-16 23:43:52.411127</PRE>
<B>Python version:</B><PRE>3.6.9 (default, Nov 7 2019, 10:44:02)
[GCC 8.3.0]</PRE>
<B>Platform:</B><PRE>Linux-4.15.0-88-generic-x86_64-with-Ubuntu-18.04-bionic</PRE>
<HR></HR>
<H2>Garbage collector:</H2>
<B>Counts:</B><PRE>(578, 10, 4)</PRE>
<B>Thresholds:</B><PRE>(700, 10, 10)</PRE>
<HR></HR>
<H2>Result of 1 checks:</H2>
<TABLE bgcolor="#ffffff" border="1">
<TBODY>
<TR>
<TH>
Kind
</TH>
<TH>
Reason
</TH>
<TH>
Details
</TH>
</TR>
<TR>
<TD>OctaviaDBCheckResult</TD>
<TD>The Octavia database is unavailable.</TD>
<TD>Database health check failed due to: (pymysql.err.OperationalError) (2003, &#34;Can&#39;t connect to MySQL server on &#39;127.0.0.1&#39; ([Errno 111] Connection refused)&#34;)
[SQL: SELECT 1]
(Background on this error at: http://sqlalche.me/e/e3q8).</TD>
</TR>
</TBODY>
</TABLE>
<HR></HR>
<H2>1 greenthread(s) active:</H2>
<TABLE bgcolor="#ffffff" border="1">
<TBODY>
<TR>
<TD><PRE> <...> </PRE></TD>
</TR>
</TBODY>
</TABLE>
<HR></HR>
<H2>1 thread(s) active:</H2>
<TABLE bgcolor="#ffffff" border="1">
<TBODY>
<TR>
<TD><PRE> <...> </PRE></TD>
</TR>
</TBODY>
</TABLE>
</BODY>
</HTML>
Example passing output for application/json with *detailed* True:
.. code-block:: bash
$ curl -i -H "Accept: application/json" http://192.51.100.10/load-balancer/healthcheck
HTTP/1.1 200 OK
Date: Mon, 16 Mar 2020 22:05:26 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: application/json
Content-Length: 9298
x-openstack-request-id: req-d3913655-6e3f-4086-a252-8bb297ea5fd6
Connection: close
{
"detailed": true,
"gc": {
"counts": [
27,
10,
4
],
"threshold": [
700,
10,
10
]
},
"greenthreads": [
<...>
],
"now": "2020-03-16 22:05:26.431429",
"platform": "Linux-4.15.0-88-generic-x86_64-with-Ubuntu-18.04-bionic",
"python_version": "3.6.9 (default, Nov 7 2019, 10:44:02) \n[GCC 8.3.0]",
"reasons": [
{
"class": "OctaviaDBCheckResult",
"details": "",
"reason": "OK"
}
],
"threads": [
<...>
]
}
Example failing output for application/json with *detailed* True:
.. code-block:: bash
$ curl -i -H "Accept: application/json" http://192.51.100.10/load-balancer/healthcheck
HTTP/1.1 503 Service Unavailable
Date: Mon, 16 Mar 2020 23:56:43 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: application/json
Content-Length: 9510
x-openstack-request-id: req-3d62ea04-9bdb-4e19-b218-1a81ff7d7337
Connection: close
{
"detailed": true,
"gc": {
"counts": [
178,
0,
5
],
"threshold": [
700,
10,
10
]
},
"greenthreads": [
<...>
],
"now": "2020-03-16 23:58:23.361209",
"platform": "Linux-4.15.0-88-generic-x86_64-with-Ubuntu-18.04-bionic",
"python_version": "3.6.9 (default, Nov 7 2019, 10:44:02) \n[GCC 8.3.0]",
"reasons": [
{
"class": "OctaviaDBCheckResult",
"details": "(pymysql.err.OperationalError) (2003, \"Can't connect to MySQL server on '127.0.0.1' ([Errno 111] Connection refused)\")\n(Background on this error at: http://sqlalche.me/e/e3q8)",
"reason": "The Octavia database is unavailable."
}
],
"threads": [
<...>
]
}
Oslo Healthcheck Plugins
========================
The Octavia API health monitoring endpoint, implemented with Oslo middleware
healthcheck, is extensible using optional backend plugins. There are currently
plugins provided by the Oslo middleware library and plugins provided by
Octavia.
**Oslo middleware provided plugins**
* `disable_by_file <https://docs.openstack.org/oslo.middleware/latest/reference/healthcheck_plugins.html#disable-by-file>`_
* `disable_by_files_ports <https://docs.openstack.org/oslo.middleware/latest/reference/healthcheck_plugins.html#disable-by-files-ports>`_
**Octavia provided plugins**
* `octavia_db_check`_
.. warning::
Some plugins may have long timeouts. It is a best practice to configure your
healthcheck query to have connection, read, and/or data timeouts. The
appropriate values will be unique to each deployment depending on the cloud
performance, number of plugins, etc.
Enabling Octavia API Health Monitoring
======================================
To enable the Octavia API health monitoring endpoint, the proper configuration
file settings need to be updated and the Octavia API processes need to be
restarted.
Start by enabling the endpoint:
.. code-block:: ini
[api_settings]
healthcheck_enabled = True
When the healthcheck_enabled setting is *False*, queries of the /healthcheck
will receive an HTTP 404 Not Found response.
You will then need to select the desired monitoring backend plugins:
.. code-block:: ini
[healthcheck]
backends = octavia_db_check
.. note::
When no plugins are configured, the behavior of Oslo middleware healthcheck
changes. Not only does it not run any tests, it will return 204 results
instead of 200.
Optionally you can enable the "detailed" mode in Oslo middleware healthcheck.
This will cause Oslo middleware healthcheck to return additional information
about the API instance. It will also provide exception details if one was
raised during the health check. This setting is False and disabled by default
in the Octavia API.
.. code-block:: ini
[healthcheck]
detailed = True
.. warning::
Enabling the 'detailed' setting will expose sensitive details about
the API process. Do not enabled this unless you are sure it will
not pose a **security risk** to your API instances.
We highly recommend you do not enable this.
Using Octavia API Health Monitoring
===================================
The Octavia API health monitoring endpoint can be accessed via the
/healthmonitor path on the `Octavia API endpoint <https://docs.openstack.org/api-ref/load-balancer/v2/index.html#service-endpoints>`_.
For example, if your Octavia (load-balancer) endpoint in keystone is:
.. code-block:: bash
https://10.21.21.78/load-balancer
You would access the Octavia API health monitoring endpoint via:
.. code-block:: bash
https://10.21.21.78/load-balancer/healthcheck
A keystone token is not required to access this endpoint.
Octavia Plugins
===============
octavia_db_check
----------------
The octavia_db_check plugin validates the API instance has a working connection
to the Octavia database. It executes a SQL no-op query, 'SELECT 1;', against
the database.
.. note::
Many OpenStack services and libraries, such as oslo.db and sqlalchemy, also
use the no-op query, 'SELECT 1;' for health checks.
The possible octavia_db_check results are:
+---------+--------+-------------+--------------------------------------+
| Request | Result | Status Code | "reason" Message |
+=========+========+=============+======================================+
| GET | Pass | 200 | OK |
+---------+--------+-------------+--------------------------------------+
| HEAD | Pass | 204 | |
+---------+--------+-------------+--------------------------------------+
| GET | Fail | 503 | The Octavia database is unavailable. |
+---------+--------+-------------+--------------------------------------+
| HEAD | Fail | 503 | |
+---------+--------+-------------+--------------------------------------+
When running Oslo middleware healthcheck in "detailed" mode, the "details"
field will have additional information about the error encountered, including
the exception details if they were available.

View File

@ -33,6 +33,7 @@ Optional Installation and Configuration Guides
providers/index.rst
log-offloading.rst
api-audit.rst
healthcheck.rst
flavors.rst
apache-httpd.rst

View File

@ -53,6 +53,9 @@
# The minimum health monitor delay interval for UDP-CONNECT Health Monitor type
# udp_connect_min_interval_health_monitor = 3
# Boolean to enable/disable oslo middleware /healthcheck in the Octavia API
# healthcheck_enabled = False
[database]
# This line MUST be changed to actually run the plugin.
# Example:
@ -572,3 +575,21 @@
# List of enabled provider agents.
# enabled_provider_agents =
[healthcheck]
# WARNING: Enabling the 'detailed' setting will expose sensitive details about
# the API process. Do not enabled this unless you are sure it will
# not pose a security risk to your API instances.
# We highly recommend you do not enable this.
# detailed = False
# This is a list of oslo middleware healthcheck backend plugins to enable for
# the oslo middleware health check.
#
# Plugins provided by oslo middleware:
# disable_by_file
# disable_by_files_ports
# Plugins provided by Octavia:
# octavia_db_check
#
# backends =

View File

@ -86,7 +86,7 @@ oslo.db==4.27.0
oslo.i18n==3.15.3
oslo.log==3.36.0
oslo.messaging==6.3.0
oslo.middleware==3.31.0
oslo.middleware==4.0.1
oslo.policy==1.30.0
oslo.reports==1.18.0
oslo.serialization==2.18.0
@ -99,7 +99,7 @@ paramiko==2.4.1
Paste==2.0.3
PasteDeploy==1.5.2
pbr==2.0.0
pecan==1.0.0
pecan==1.3.2
pep8==1.7.1
pika==0.10.0
pika-pool==0.1.3
@ -169,7 +169,7 @@ vine==1.1.4
voluptuous==0.11.1
waitress==1.1.0
warlock==1.3.0
WebOb==1.7.1
WebOb==1.8.2
WebTest==2.0.29
Werkzeug==0.14.1
wrapt==1.10.11

View File

@ -20,7 +20,8 @@ from oslo_log import log as logging
from oslo_middleware import cors
from oslo_middleware import http_proxy_to_wsgi
from oslo_middleware import request_id
import pecan
from pecan import configuration as pecan_configuration
from pecan import make_app as pecan_make_app
from octavia.api import config as app_config
from octavia.api.drivers import driver_factory
@ -36,7 +37,7 @@ CONF = cfg.CONF
def get_pecan_config():
"""Returns the pecan config."""
filename = app_config.__file__.replace('.pyc', '.py')
return pecan.configuration.conf_from_file(filename)
return pecan_configuration.conf_from_file(filename)
def _init_drivers():
@ -56,9 +57,9 @@ def setup_app(pecan_config=None, debug=False, argv=None):
if not pecan_config:
pecan_config = get_pecan_config()
pecan.configuration.set_config(dict(pecan_config), overwrite=True)
pecan_configuration.set_config(dict(pecan_config), overwrite=True)
return pecan.make_app(
return pecan_make_app(
pecan_config.app.root,
wrap_app=_wrap_app,
debug=debug,

View File

@ -0,0 +1,47 @@
# Copyright 2020 Red Hat, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_middleware.healthcheck import pluginbase
from octavia.db import api as db_apis
from octavia.db import healthcheck
class OctaviaDBHealthcheck(pluginbase.HealthcheckBaseExtension):
UNAVAILABLE_REASON = 'The Octavia database is unavailable.'
def __init__(self, *args, **kwargs):
super(OctaviaDBHealthcheck, self).__init__(*args, **kwargs)
def healthcheck(self, server_port):
try:
result, message = healthcheck.check_database_connection(
db_apis.get_session())
if result:
return OctaviaDBCheckResult(available=True, reason="OK")
else:
return OctaviaDBCheckResult(available=False,
reason=self.UNAVAILABLE_REASON,
details=message)
except Exception as e:
return OctaviaDBCheckResult(available=False,
reason=self.UNAVAILABLE_REASON,
details=str(e))
class OctaviaDBCheckResult(pluginbase.HealthcheckResult):
"""Result sub-class to provide a unique name in detail reports."""
def __init__(self, *args, **kwargs):
super(OctaviaDBCheckResult, self).__init__(*args, **kwargs)

View File

@ -12,25 +12,41 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from oslo_log import log as logging
from oslo_middleware import healthcheck
from pecan import abort as pecan_abort
from pecan import expose as pecan_expose
from pecan import request as pecan_request
from pecan import rest
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
from octavia.api.v2 import controllers as v2_controller
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class RootController(rest.RestController):
class RootController(object):
"""The controller with which the pecan wsgi app should be created."""
def __init__(self):
super(RootController, self).__init__()
setattr(self, 'v2.0', v2_controller.V2Controller())
setattr(self, 'v2', v2_controller.V2Controller())
if CONF.api_settings.healthcheck_enabled:
self.healthcheck_obj = healthcheck.Healthcheck.app_factory(None)
# Run the oslo middleware healthcheck for /healthcheck
@pecan_expose('json')
@pecan_expose(content_type='plain/text')
@pecan_expose(content_type='text/html')
def healthcheck(self): # pylint: disable=inconsistent-return-statements
if CONF.api_settings.healthcheck_enabled:
if pecan_request.method not in ['GET', 'HEAD']:
pecan_abort(405)
return self.healthcheck_obj.process_request(pecan_request)
pecan_abort(404)
def _add_a_version(self, versions, version, url_version, status,
timestamp, base_url):
@ -45,7 +61,7 @@ class RootController(rest.RestController):
})
@wsme_pecan.wsexpose(wtypes.text)
def get(self):
def index(self):
host_url = pecan_request.path_url
if not host_url.endswith('/'):

View File

@ -17,7 +17,8 @@ from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging as messaging
from oslo_utils import excutils
import pecan
from pecan import expose as pecan_expose
from pecan import request as pecan_request
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -41,7 +42,7 @@ class AmphoraController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None):
"""Gets a single amphora's details."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_amp = self._get_db_amp(context.session, id, show_deleted=False)
self._auth_validate_action(context, context.project_id,
@ -57,7 +58,7 @@ class AmphoraController(base.BaseController):
ignore_extra_args=True)
def get_all(self, fields=None):
"""Gets all health monitors."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
self._auth_validate_action(context, context.project_id,
@ -73,7 +74,7 @@ class AmphoraController(base.BaseController):
return amp_types.AmphoraeRootResponse(
amphorae=result, amphorae_links=links)
@pecan.expose()
@pecan_expose()
def _lookup(self, amphora_id, *remainder):
"""Overridden pecan _lookup method for custom routing.
@ -107,7 +108,7 @@ class FailoverController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=202)
def put(self):
"""Fails over an amphora"""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
db_amp = self._get_db_amp(context.session, self.amp_id,
show_deleted=False)
@ -153,7 +154,7 @@ class AmphoraUpdateController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=202)
def put(self):
"""Update amphora agent configuration"""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
db_amp = self._get_db_amp(context.session, self.amp_id,
show_deleted=False)
@ -188,7 +189,7 @@ class AmphoraStatsController(base.BaseController):
@wsme_pecan.wsexpose(amp_types.StatisticsRootResponse, wtypes.text,
status_code=200)
def get(self):
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_STATS)

View File

@ -17,7 +17,7 @@ from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import excutils
from oslo_utils import uuidutils
import pecan
from pecan import request as pecan_request
from sqlalchemy.orm import exc as sa_exception
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -43,7 +43,7 @@ class AvailabilityZoneProfileController(base.BaseController):
wtypes.text, [wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None):
"""Gets an Availability Zone Profile's detail."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ONE)
if id == constants.NIL_UUID:
@ -63,7 +63,7 @@ class AvailabilityZoneProfileController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_all(self, fields=None):
"""Lists all Availability Zone Profiles."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ALL)
@ -87,7 +87,7 @@ class AvailabilityZoneProfileController(base.BaseController):
"""Creates an Availability Zone Profile."""
availability_zone_profile = (
availability_zone_profile_.availability_zone_profile)
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_POST)
# Do a basic JSON validation on the metadata
@ -155,7 +155,7 @@ class AvailabilityZoneProfileController(base.BaseController):
"""Updates an Availability Zone Profile."""
availability_zone_profile = (
availability_zone_profile_.availability_zone_profile)
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_PUT)
@ -216,7 +216,7 @@ class AvailabilityZoneProfileController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, availability_zone_profile_id):
"""Deletes an Availability Zone Profile"""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_DELETE)

View File

@ -16,7 +16,7 @@ from oslo_db import api as oslo_db_api
from oslo_db import exception as odb_exceptions
from oslo_log import log as logging
from oslo_utils import excutils
import pecan
from pecan import request as pecan_request
from sqlalchemy.orm import exc as sa_exception
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -40,7 +40,7 @@ class AvailabilityZonesController(base.BaseController):
wtypes.text, [wtypes.text], ignore_extra_args=True)
def get_one(self, name, fields=None):
"""Gets an Availability Zone's detail."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ONE)
if name == constants.NIL_UUID:
@ -60,7 +60,7 @@ class AvailabilityZonesController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_all(self, fields=None):
"""Lists all Availability Zones."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ALL)
@ -82,7 +82,7 @@ class AvailabilityZonesController(base.BaseController):
def post(self, availability_zone_):
"""Creates an Availability Zone."""
availability_zone = availability_zone_.availability_zone
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_POST)
@ -111,7 +111,7 @@ class AvailabilityZonesController(base.BaseController):
body=availability_zone_types.AvailabilityZoneRootPUT)
def put(self, name, availability_zone_):
availability_zone = availability_zone_.availability_zone
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_PUT)
if name == constants.NIL_UUID:
@ -144,7 +144,7 @@ class AvailabilityZonesController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, availability_zone_name):
"""Deletes an Availability Zone"""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_DELETE)

View File

@ -16,7 +16,8 @@ from cryptography.hazmat.backends import default_backend
from cryptography import x509
from oslo_config import cfg
from oslo_log import log as logging
import pecan
from pecan import request as pecan_request
from pecan import rest as pecan_rest
from stevedore import driver as stevedore_driver
from wsme import types as wtypes
@ -31,7 +32,7 @@ CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class BaseController(pecan.rest.RestController):
class BaseController(pecan_rest.RestController):
RBAC_TYPE = None
def __init__(self):
@ -257,7 +258,7 @@ class BaseController(pecan.rest.RestController):
return attrs
def _validate_tls_refs(self, tls_refs):
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
bad_refs = []
for ref in tls_refs:
try:
@ -272,7 +273,7 @@ class BaseController(pecan.rest.RestController):
raise exceptions.CertificateRetrievalException(ref=bad_refs)
def _validate_client_ca_and_crl_refs(self, client_ca_ref, crl_ref):
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
bad_refs = []
try:
self.cert_manager.set_acls(context, client_ca_ref)

View File

@ -18,7 +18,7 @@ from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import excutils
from oslo_utils import uuidutils
import pecan
from pecan import request as pecan_request
from sqlalchemy.orm import exc as sa_exception
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -44,7 +44,7 @@ class FlavorProfileController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None):
"""Gets a flavor profile's detail."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ONE)
if id == constants.NIL_UUID:
@ -61,7 +61,7 @@ class FlavorProfileController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_all(self, fields=None):
"""Lists all flavor profiles."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ALL)
@ -81,7 +81,7 @@ class FlavorProfileController(base.BaseController):
def post(self, flavor_profile_):
"""Creates a flavor Profile."""
flavorprofile = flavor_profile_.flavorprofile
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_POST)
# Do a basic JSON validation on the metadata
@ -138,7 +138,7 @@ class FlavorProfileController(base.BaseController):
def put(self, id, flavor_profile_):
"""Updates a flavor Profile."""
flavorprofile = flavor_profile_.flavorprofile
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_PUT)
@ -190,7 +190,7 @@ class FlavorProfileController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, flavor_profile_id):
"""Deletes a Flavor Profile"""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_DELETE)

View File

@ -18,7 +18,7 @@ from oslo_db import exception as odb_exceptions
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import uuidutils
import pecan
from pecan import request as pecan_request
from sqlalchemy.orm import exc as sa_exception
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -42,7 +42,7 @@ class FlavorsController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None):
"""Gets a flavor's detail."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ONE)
if id == constants.NIL_UUID:
@ -58,7 +58,7 @@ class FlavorsController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_all(self, fields=None):
"""Lists all flavors."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ALL)
@ -77,7 +77,7 @@ class FlavorsController(base.BaseController):
def post(self, flavor_):
"""Creates a flavor."""
flavor = flavor_.flavor
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_POST)
@ -106,7 +106,7 @@ class FlavorsController(base.BaseController):
body=flavor_types.FlavorRootPUT)
def put(self, id, flavor_):
flavor = flavor_.flavor
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_PUT)
if id == constants.NIL_UUID:
@ -134,7 +134,7 @@ class FlavorsController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, flavor_id):
"""Deletes a Flavor"""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_DELETE)

View File

@ -17,7 +17,7 @@ from oslo_config import cfg
from oslo_db import exception as odb_exceptions
from oslo_log import log as logging
from oslo_utils import excutils
import pecan
from pecan import request as pecan_request
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -48,7 +48,7 @@ class HealthMonitorController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None):
"""Gets a single healthmonitor's details."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_hm = self._get_db_hm(context.session, id, show_deleted=False)
self._auth_validate_action(context, db_hm.project_id,
@ -64,7 +64,7 @@ class HealthMonitorController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_all(self, project_id=None, fields=None):
"""Gets all health monitors."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
query_filter = self._auth_get_all(context, project_id)
@ -196,7 +196,7 @@ class HealthMonitorController(base.BaseController):
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')
context = pecan_request.context.get('octavia_context')
health_monitor = health_monitor_.healthmonitor
if (not CONF.api_settings.allow_ping_health_monitors and
@ -334,7 +334,7 @@ class HealthMonitorController(base.BaseController):
body=hm_types.HealthMonitorRootPUT, status_code=200)
def put(self, id, health_monitor_):
"""Updates a health monitor."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
health_monitor = health_monitor_.healthmonitor
db_hm = self._get_db_hm(context.session, id, show_deleted=False)
@ -393,7 +393,7 @@ class HealthMonitorController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, id):
"""Deletes a health monitor."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_hm = self._get_db_hm(context.session, id, show_deleted=False)
pool = self._get_db_pool(context.session, db_hm.pool_id)

View File

@ -16,7 +16,8 @@ from oslo_config import cfg
from oslo_db import exception as odb_exceptions
from oslo_log import log as logging
from oslo_utils import excutils
import pecan
from pecan import expose as pecan_expose
from pecan import request as pecan_request
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -48,7 +49,7 @@ class L7PolicyController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get(self, id, fields=None):
"""Gets a single l7policy's details."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_l7policy = self._get_db_l7policy(context.session, id,
show_deleted=False)
@ -65,7 +66,7 @@ class L7PolicyController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_all(self, project_id=None, fields=None):
"""Lists all l7policies of a listener."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
query_filter = self._auth_get_all(context, project_id)
@ -115,7 +116,7 @@ class L7PolicyController(base.BaseController):
def post(self, l7policy_):
"""Creates a l7policy on a listener."""
l7policy = l7policy_.l7policy
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
# Verify the parent listener exists
listener_id = l7policy.listener_id
listener = self._get_db_listener(
@ -206,7 +207,7 @@ class L7PolicyController(base.BaseController):
if val in l7policy_dict:
l7policy_dict[attr] = l7policy_dict.pop(val)
sanitized_l7policy = l7policy_types.L7PolicyPUT(**l7policy_dict)
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_l7policy = self._get_db_l7policy(context.session, id,
show_deleted=False)
@ -268,7 +269,7 @@ class L7PolicyController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, id):
"""Deletes a l7policy."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_l7policy = self._get_db_l7policy(context.session, id,
show_deleted=False)
load_balancer_id, listener_id = self._get_listener_and_loadbalancer_id(
@ -300,14 +301,14 @@ class L7PolicyController(base.BaseController):
driver_utils.call_provider(driver.name, driver.l7policy_delete,
provider_l7policy)
@pecan.expose()
@pecan_expose()
def _lookup(self, l7policy_id, *remainder):
"""Overridden pecan _lookup method for custom routing.
Verifies that the l7policy passed in the url exists, and if so decides
which controller, if any, should control be passed.
"""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
if l7policy_id and remainder and remainder[0] == 'rules':
remainder = remainder[1:]
db_l7policy = self.repositories.l7policy.get(

View File

@ -15,7 +15,7 @@
from oslo_db import exception as odb_exceptions
from oslo_log import log as logging
from oslo_utils import excutils
import pecan
from pecan import request as pecan_request
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -46,7 +46,7 @@ class L7RuleController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get(self, id, fields=None):
"""Gets a single l7rule's details."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_l7rule = self._get_db_l7rule(context.session, id,
show_deleted=False)
@ -63,7 +63,7 @@ class L7RuleController(base.BaseController):
ignore_extra_args=True)
def get_all(self, fields=None):
"""Lists all l7rules of a l7policy."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
l7policy = self._get_db_l7policy(context.session, self.l7policy_id,
@ -127,7 +127,7 @@ class L7RuleController(base.BaseController):
validate.l7rule_data(l7rule)
except Exception as e:
raise exceptions.L7RuleValidation(error=e)
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_l7policy = self._get_db_l7policy(context.session, self.l7policy_id,
show_deleted=False)
@ -189,7 +189,7 @@ class L7RuleController(base.BaseController):
def put(self, id, l7rule_):
"""Updates a l7rule."""
l7rule = l7rule_.rule
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_l7rule = self._get_db_l7rule(context.session, id,
show_deleted=False)
@ -256,7 +256,7 @@ class L7RuleController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, id):
"""Deletes a l7rule."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_l7rule = self._get_db_l7rule(context.session, id,
show_deleted=False)

View File

@ -17,7 +17,8 @@ from oslo_config import cfg
from oslo_db import exception as odb_exceptions
from oslo_log import log as logging
from oslo_utils import excutils
import pecan
from pecan import expose as pecan_expose
from pecan import request as pecan_request
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -51,7 +52,7 @@ class ListenersController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None):
"""Gets a single listener's details."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_listener = self._get_db_listener(context.session, id,
show_deleted=False)
@ -72,7 +73,7 @@ class ListenersController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_all(self, project_id=None, fields=None):
"""Lists all listeners."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
query_filter = self._auth_get_all(context, project_id)
@ -237,7 +238,7 @@ class ListenersController(base.BaseController):
# Validate that the L4 protocol (UDP or TCP) is not already used for
# the specified protocol_port in this load balancer
pcontext = pecan.request.context
pcontext = pecan_request.context
query_filter = {
'project_id': listener_dict['project_id'],
'load_balancer_id': listener_dict['load_balancer_id'],
@ -310,7 +311,7 @@ class ListenersController(base.BaseController):
def post(self, listener_):
"""Creates a listener on a load balancer."""
listener = listener_.listener
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
load_balancer_id = listener.loadbalancer_id
listener.project_id, provider = self._get_lb_project_id_provider(
@ -507,7 +508,7 @@ class ListenersController(base.BaseController):
def put(self, id, listener_):
"""Updates a listener on a load balancer."""
listener = listener_.listener
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_listener = self._get_db_listener(context.session, id,
show_deleted=False)
load_balancer_id = db_listener.load_balancer_id
@ -567,7 +568,7 @@ class ListenersController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, id):
"""Deletes a listener from a load balancer."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_listener = self._get_db_listener(context.session, id,
show_deleted=False)
load_balancer_id = db_listener.load_balancer_id
@ -594,7 +595,7 @@ class ListenersController(base.BaseController):
driver_utils.call_provider(driver.name, driver.listener_delete,
provider_listener)
@pecan.expose()
@pecan_expose()
def _lookup(self, id, *remainder):
"""Overridden pecan _lookup method for custom routing.
@ -616,7 +617,7 @@ class StatisticsController(base.BaseController, stats.StatsMixin):
@wsme_pecan.wsexpose(listener_types.StatisticsRootResponse, wtypes.text,
status_code=200)
def get(self):
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_listener = self._get_db_listener(context.session, self.id,
show_deleted=False)
if not db_listener:

View File

@ -18,7 +18,8 @@ from oslo_db import exception as odb_exceptions
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import strutils
import pecan
from pecan import expose as pecan_expose
from pecan import request as pecan_request
from sqlalchemy.orm import exc as sa_exception
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -56,7 +57,7 @@ class LoadBalancersController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None):
"""Gets a single load balancer's details."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
load_balancer = self._get_db_lb(context.session, id,
show_deleted=False)
@ -78,7 +79,7 @@ class LoadBalancersController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_all(self, project_id=None, fields=None):
"""Lists all load balancers."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
query_filter = self._auth_get_all(context, project_id)
@ -373,7 +374,7 @@ class LoadBalancersController(base.BaseController):
def post(self, load_balancer):
"""Creates a load balancer."""
load_balancer = load_balancer.loadbalancer
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
if not load_balancer.project_id and context.project_id:
load_balancer.project_id = context.project_id
@ -607,7 +608,7 @@ class LoadBalancersController(base.BaseController):
def put(self, id, load_balancer):
"""Updates a load balancer."""
load_balancer = load_balancer.loadbalancer
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_lb = self._get_db_lb(context.session, id, show_deleted=False)
self._auth_validate_action(context, db_lb.project_id,
@ -665,7 +666,7 @@ class LoadBalancersController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, wtypes.text, status_code=204)
def delete(self, id, cascade=False):
"""Deletes a load balancer."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
cascade = strutils.bool_from_string(cascade)
db_lb = self._get_db_lb(context.session, id, show_deleted=False)
@ -692,7 +693,7 @@ class LoadBalancersController(base.BaseController):
driver_utils.call_provider(driver.name, driver.loadbalancer_delete,
provider_loadbalancer, cascade)
@pecan.expose()
@pecan_expose()
def _lookup(self, id, *remainder):
"""Overridden pecan _lookup method for custom routing.
@ -731,7 +732,7 @@ class StatusController(base.BaseController):
@wsme_pecan.wsexpose(lb_types.StatusRootResponse, wtypes.text,
status_code=200)
def get(self):
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
load_balancer = self._get_db_lb(context.session, self.id,
show_deleted=False)
if not load_balancer:
@ -759,7 +760,7 @@ class StatisticsController(base.BaseController, stats.StatsMixin):
@wsme_pecan.wsexpose(lb_types.StatisticsRootResponse, wtypes.text,
status_code=200)
def get(self):
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
load_balancer = self._get_db_lb(context.session, self.id,
show_deleted=False)
if not load_balancer:
@ -787,7 +788,7 @@ class FailoverController(LoadBalancersController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=202)
def put(self, **kwargs):
"""Fails over a loadbalancer"""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_lb = self._get_db_lb(context.session, self.lb_id,
show_deleted=False)

View File

@ -17,7 +17,7 @@ from oslo_db import exception as odb_exceptions
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import strutils
import pecan
from pecan import request as pecan_request
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -48,7 +48,7 @@ class MemberController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get(self, id, fields=None):
"""Gets a single pool member's details."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_member = self._get_db_member(context.session, id,
show_deleted=False)
@ -67,7 +67,7 @@ class MemberController(base.BaseController):
ignore_extra_args=True)
def get_all(self, fields=None):
"""Lists all pool members of a pool."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
pool = self._get_db_pool(context.session, self.pool_id,
@ -144,7 +144,7 @@ class MemberController(base.BaseController):
def post(self, member_):
"""Creates a pool member on a pool."""
member = member_.member
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
validate.ip_not_reserved(member.address)
@ -229,7 +229,7 @@ class MemberController(base.BaseController):
def put(self, id, member_):
"""Updates a pool member."""
member = member_.member
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_member = self._get_db_member(context.session, id,
show_deleted=False)
@ -285,7 +285,7 @@ class MemberController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, id):
"""Deletes a pool member."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_member = self._get_db_member(context.session, id,
show_deleted=False)
@ -327,7 +327,7 @@ class MembersController(MemberController):
"""Updates all members."""
members = members_.members
additive_only = strutils.bool_from_string(additive_only)
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_pool = self._get_db_pool(context.session, self.pool_id)
old_members = db_pool.members

View File

@ -17,7 +17,8 @@ from oslo_config import cfg
from oslo_db import exception as odb_exceptions
from oslo_log import log as logging
from oslo_utils import excutils
import pecan
from pecan import expose as pecan_expose
from pecan import request as pecan_request
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -51,7 +52,7 @@ class PoolsController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get(self, id, fields=None):
"""Gets a pool's details."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_pool = self._get_db_pool(context.session, id, show_deleted=False)
self._auth_validate_action(context, db_pool.project_id,
@ -66,7 +67,7 @@ class PoolsController(base.BaseController):
[wtypes.text], ignore_extra_args=True)
def get_all(self, project_id=None, fields=None):
"""Lists all pools."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
query_filter = self._auth_get_all(context, project_id)
@ -188,7 +189,7 @@ class PoolsController(base.BaseController):
# For some API requests the listener_id will be passed in the
# pool_dict:
pool = pool_.pool
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
if pool.protocol == constants.PROTOCOL_UDP:
self._validate_pool_request_for_udp(pool)
else:
@ -372,7 +373,7 @@ class PoolsController(base.BaseController):
def put(self, id, pool_):
"""Updates a pool on a load balancer."""
pool = pool_.pool
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_pool = self._get_db_pool(context.session, id, show_deleted=False)
project_id, provider = self._get_lb_project_id_provider(
@ -429,7 +430,7 @@ class PoolsController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, id):
"""Deletes a pool from a load balancer."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
db_pool = self._get_db_pool(context.session, id, show_deleted=False)
if db_pool.l7policies:
raise exceptions.PoolInUseByL7Policy(
@ -458,14 +459,14 @@ class PoolsController(base.BaseController):
driver_utils.call_provider(driver.name, driver.pool_delete,
provider_pool)
@pecan.expose()
@pecan_expose()
def _lookup(self, pool_id, *remainder):
"""Overridden pecan _lookup method for custom routing.
Verifies that the pool passed in the url exists, and if so decides
which controller, if any, should control be passed.
"""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
if pool_id and remainder and remainder[0] == 'members':
remainder = remainder[1:]
db_pool = self.repositories.pool.get(context.session, id=pool_id)

View File

@ -14,7 +14,8 @@
from oslo_config import cfg
from oslo_log import log as logging
import pecan
from pecan import expose as pecan_expose
from pecan import request as pecan_request
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -39,7 +40,7 @@ class ProviderController(base.BaseController):
ignore_extra_args=True)
def get_all(self, fields=None):
"""List enabled provider drivers and their descriptions."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
self._auth_validate_action(context, context.project_id,
@ -53,7 +54,7 @@ class ProviderController(base.BaseController):
response_list = self._filter_fields(response_list, fields)
return provider_types.ProvidersRootResponse(providers=response_list)
@pecan.expose()
@pecan_expose()
def _lookup(self, provider, *remainder):
"""Overridden pecan _lookup method for custom routing.
@ -82,7 +83,7 @@ class FlavorCapabilitiesController(base.BaseController):
[wtypes.text], ignore_extra_args=True,
status_code=200)
def get_all(self, fields=None):
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ALL)
self.driver = driver_factory.get_driver(self.provider)
@ -97,7 +98,7 @@ class FlavorCapabilitiesController(base.BaseController):
# Apply any valid filters provided as URL parameters
name_filter = None
description_filter = None
pagination_helper = pecan.request.context.get(
pagination_helper = pecan_request.context.get(
constants.PAGINATION_HELPER)
if pagination_helper:
name_filter = pagination_helper.params.get(constants.NAME)
@ -132,7 +133,7 @@ class AvailabilityZoneCapabilitiesController(base.BaseController):
[wtypes.text], ignore_extra_args=True,
status_code=200)
def get_all(self, fields=None):
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ALL)
self.driver = driver_factory.get_driver(self.provider)
@ -149,7 +150,7 @@ class AvailabilityZoneCapabilitiesController(base.BaseController):
# Apply any valid filters provided as URL parameters
name_filter = None
description_filter = None
pagination_helper = pecan.request.context.get(
pagination_helper = pecan_request.context.get(
constants.PAGINATION_HELPER)
if pagination_helper:
name_filter = pagination_helper.params.get(constants.NAME)

View File

@ -13,7 +13,8 @@
# under the License.
from oslo_config import cfg
import pecan
from pecan import expose as pecan_expose
from pecan import request as pecan_request
from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan
@ -35,7 +36,7 @@ class QuotasController(base.BaseController):
@wsme_pecan.wsexpose(quota_types.QuotaResponse, wtypes.text)
def get(self, project_id):
"""Get a single project's quota details."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
self._auth_validate_action(context, project_id, constants.RBAC_GET_ONE)
@ -46,7 +47,7 @@ class QuotasController(base.BaseController):
ignore_extra_args=True)
def get_all(self, project_id=None):
"""List all non-default quotas."""
pcontext = pecan.request.context
pcontext = pecan_request.context
context = pcontext.get('octavia_context')
query_filter = self._auth_get_all(context, project_id)
@ -63,7 +64,7 @@ class QuotasController(base.BaseController):
body=quota_types.QuotaPUT, status_code=202)
def put(self, project_id, quotas):
"""Update any or all quotas for a project."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
if not project_id:
raise exceptions.MissingAPIProjectID()
@ -79,7 +80,7 @@ class QuotasController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=202)
def delete(self, project_id):
"""Reset a project's quotas to the default values."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
if not project_id:
raise exceptions.MissingAPIProjectID()
@ -90,7 +91,7 @@ class QuotasController(base.BaseController):
db_quotas = self._get_db_quotas(context.session, project_id)
return self._convert_db_to_type(db_quotas, quota_types.QuotaResponse)
@pecan.expose()
@pecan_expose()
def _lookup(self, project_id, *remainder):
"""Overridden pecan _lookup method for routing default endpoint."""
if project_id and remainder and remainder[0] == 'default':
@ -108,7 +109,7 @@ class QuotasDefaultController(base.BaseController):
@wsme_pecan.wsexpose(quota_types.QuotaResponse, wtypes.text)
def get(self):
"""Get a project's default quota details."""
context = pecan.request.context.get('octavia_context')
context = pecan_request.context.get('octavia_context')
if not self.project_id:
raise exceptions.MissingAPIProjectID()

View File

@ -100,6 +100,9 @@ api_opts = [
help=_("The minimum health monitor delay interval for the "
"UDP-CONNECT Health Monitor type. A negative integer "
"value means 'no limit'.")),
cfg.BoolOpt('healthcheck_enabled', default=False,
help=_("When True, the oslo middleware healthcheck endpoint "
"is enabled in the Octavia API.")),
]
# Options only used by the amphora agent

View File

@ -21,7 +21,8 @@ from octavia.common import constants
LOG = logging.getLogger(__name__)
_NOAUTH_PATHS = ['/', '/load-balancer/']
_NOAUTH_PATHS = ['/', '/load-balancer/', '/healthcheck',
'/load-balancer/healthcheck']
class KeystoneSession(object):

37
octavia/db/healthcheck.py Normal file
View File

@ -0,0 +1,37 @@
# Copyright 2020 Red Hat, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from octavia.i18n import _
LOG = logging.getLogger(__name__)
def check_database_connection(session):
"""This is a simple database connection check function.
It will do a simple no-op query (low overhead) against the sqlalchemy
session passed in.
:param session: A Sql Alchemy database session.
:returns: True if the connection check is successful, False if not.
"""
try:
session.execute('SELECT 1;')
return True, None
except Exception as e:
message = _('Database health check failed due to: {err}.').format(
err=str(e))
LOG.error(message)
return False, message

View File

@ -0,0 +1,261 @@
# Copyright 2020 Red Hat, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from oslo_config import cfg
from oslo_config import fixture as oslo_fixture
import pecan
from octavia.api import config as pconfig
from octavia.api.healthcheck import healthcheck_plugins
from octavia.tests.functional.db import base as base_db_test
class TestHealthCheck(base_db_test.OctaviaDBTestBase):
def setUp(self):
super(TestHealthCheck, self).setUp()
# We need to define these early as they are late loaded in oslo
# middleware and our configuration overrides would not apply.
# Note: These must match exactly the option definitions in
# oslo.middleware healthcheck! If not you will get duplicate option
# errors.
healthcheck_opts = [
cfg.BoolOpt(
'detailed', default=False,
help='Show more detailed information as part of the response. '
'Security note: Enabling this option may expose '
'sensitive details about the service being monitored. '
'Be sure to verify that it will not violate your '
'security policies.'),
cfg.ListOpt(
'backends', default=[],
help='Additional backends that can perform health checks and '
'report that information back as part of a request.'),
]
cfg.CONF.register_opts(healthcheck_opts, group='healthcheck')
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
self.conf.config(group='healthcheck', backends=['octavia_db_check'])
self.UNAVAILABLE = (healthcheck_plugins.OctaviaDBHealthcheck.
UNAVAILABLE_REASON)
def reset_pecan():
pecan.set_config({}, overwrite=True)
self.addCleanup(reset_pecan)
def _make_app(self):
# Note: we need to set argv=() to stop the wsgi setup_app from
# pulling in the testing tool sys.argv
return pecan.testing.load_test_app({'app': pconfig.app,
'wsme': pconfig.wsme}, argv=())
def _get_enabled_app(self):
self.conf.config(group='api_settings', healthcheck_enabled=True)
return self._make_app()
def _get_disabled_app(self):
self.conf.config(group='api_settings', healthcheck_enabled=False)
return self._make_app()
def _get(self, app, path, params=None, headers=None, status=200,
expect_errors=False):
response = app.get(path, params=params, headers=headers, status=status,
expect_errors=expect_errors)
return response
def _head(self, app, path, headers=None, status=204, expect_errors=False):
response = app.head(path, headers=headers, status=status,
expect_errors=expect_errors)
return response
def _post(self, app, path, body, headers=None, status=201,
expect_errors=False):
response = app.post_json(path, params=body, headers=headers,
status=status, expect_errors=expect_errors)
return response
def _put(self, app, path, body, headers=None, status=200,
expect_errors=False):
response = app.put_json(path, params=body, headers=headers,
status=status, expect_errors=expect_errors)
return response
def _delete(self, app, path, params=None, headers=None, status=204,
expect_errors=False):
response = app.delete(path, headers=headers, status=status,
expect_errors=expect_errors)
return response
def test_healthcheck_get_text(self):
self.conf.config(group='healthcheck', detailed=False)
response = self._get(self._get_enabled_app(), '/healthcheck')
self.assertEqual(200, response.status_code)
self.assertEqual('OK', response.text)
# Note: For whatever reason, detailed=True text has no additonal info
def test_healthcheck_get_text_detailed(self):
self.conf.config(group='healthcheck', detailed=True)
response = self._get(self._get_enabled_app(), '/healthcheck')
self.assertEqual(200, response.status_code)
self.assertEqual('OK', response.text)
def test_healthcheck_get_json(self):
self.conf.config(group='healthcheck', detailed=False)
response = self._get(self._get_enabled_app(), '/healthcheck',
headers={'Accept': 'application/json'})
self.assertEqual(200, response.status_code)
self.assertFalse(response.json['detailed'])
self.assertEqual(['OK'], response.json['reasons'])
def test_healthcheck_get_json_detailed(self):
self.conf.config(group='healthcheck', detailed=True)
response = self._get(self._get_enabled_app(), '/healthcheck',
headers={'Accept': 'application/json'})
self.assertEqual(200, response.status_code)
self.assertTrue(response.json['detailed'])
self.assertEqual('OK', response.json['reasons'][0]['reason'])
self.assertTrue(response.json['gc'])
def test_healthcheck_get_html(self):
self.conf.config(group='healthcheck', detailed=False)
response = self._get(self._get_enabled_app(), '/healthcheck',
headers={'Accept': 'text/html'})
self.assertEqual(200, response.status_code)
self.assertIn('OK', response.text)
def test_healthcheck_get_html_detailed(self):
self.conf.config(group='healthcheck', detailed=True)
response = self._get(self._get_enabled_app(), '/healthcheck',
headers={'Accept': 'text/html'})
self.assertEqual(200, response.status_code)
self.assertIn('OK', response.text)
self.assertIn('Garbage collector', response.text)
def test_healthcheck_disabled_get(self):
self._get(self._get_disabled_app(), '/healthcheck', status=404)
def test_healthcheck_head(self):
response = self._head(self._get_enabled_app(), '/healthcheck')
self.assertEqual(204, response.status_code)
def test_healthcheck_disabled_head(self):
self._head(self._get_disabled_app(), '/healthcheck', status=404)
# These should be denied by the API
def test_healthcheck_post(self):
self._post(self._get_enabled_app(), '/healthcheck',
{'foo': 'bar'}, status=405)
def test_healthcheck_put(self):
self._put(self._get_enabled_app(), '/healthcheck',
{'foo': 'bar'}, status=405)
def test_healthcheck_delete(self):
self._delete(self._get_enabled_app(), '/healthcheck',
status=405)
@mock.patch('octavia.db.api.get_session')
def test_healthcheck_get_failed(self, mock_get_session):
mock_session = mock.MagicMock()
mock_session.execute.side_effect = [Exception('boom')]
mock_get_session.return_value = mock_session
response = self._get(self._get_enabled_app(), '/healthcheck',
status=503)
self.assertEqual(503, response.status_code)
self.assertEqual(self.UNAVAILABLE, response.text)
@mock.patch('octavia.db.api.get_session')
def test_healthcheck_head_failed(self, mock_get_session):
mock_session = mock.MagicMock()
mock_session.execute.side_effect = [Exception('boom')]
mock_get_session.return_value = mock_session
response = self._head(self._get_enabled_app(), '/healthcheck',
status=503)
self.assertEqual(503, response.status_code)
@mock.patch('octavia.db.healthcheck.check_database_connection',
side_effect=Exception('boom'))
def test_healthcheck_get_failed_check(self, mock_db_check):
response = self._get(self._get_enabled_app(), '/healthcheck',
status=503)
self.assertEqual(503, response.status_code)
self.assertEqual(self.UNAVAILABLE, response.text)
@mock.patch('octavia.db.api.get_session')
def test_healthcheck_get_json_failed(self, mock_get_session):
self.conf.config(group='healthcheck', detailed=False)
mock_session = mock.MagicMock()
mock_session.execute.side_effect = [Exception('boom')]
mock_get_session.return_value = mock_session
response = self._get(self._get_enabled_app(), '/healthcheck',
headers={'Accept': 'application/json'},
status=503)
self.assertEqual(503, response.status_code)
self.assertFalse(response.json['detailed'])
self.assertEqual([self.UNAVAILABLE],
response.json['reasons'])
@mock.patch('octavia.db.api.get_session')
def test_healthcheck_get_json_detailed_failed(self, mock_get_session):
self.conf.config(group='healthcheck', detailed=True)
mock_session = mock.MagicMock()
mock_session.execute.side_effect = [Exception('boom')]
mock_get_session.return_value = mock_session
response = self._get(self._get_enabled_app(), '/healthcheck',
headers={'Accept': 'application/json'},
status=503)
self.assertEqual(503, response.status_code)
self.assertTrue(response.json['detailed'])
self.assertEqual(self.UNAVAILABLE,
response.json['reasons'][0]['reason'])
self.assertIn('boom', response.json['reasons'][0]['details'])
@mock.patch('octavia.db.api.get_session')
def test_healthcheck_get_html_failed(self, mock_get_session):
self.conf.config(group='healthcheck', detailed=False)
mock_session = mock.MagicMock()
mock_session.execute.side_effect = [Exception('boom')]
mock_get_session.return_value = mock_session
response = self._get(self._get_enabled_app(), '/healthcheck',
headers={'Accept': 'text/html'}, status=503)
self.assertEqual(503, response.status_code)
self.assertIn(self.UNAVAILABLE, response.text)
@mock.patch('octavia.db.api.get_session')
def test_healthcheck_get_html_detailed_failed(self, mock_get_session):
self.conf.config(group='healthcheck', detailed=True)
mock_session = mock.MagicMock()
mock_session.execute.side_effect = [Exception('boom')]
mock_get_session.return_value = mock_session
response = self._get(self._get_enabled_app(), '/healthcheck',
headers={'Accept': 'text/html'}, status=503)
self.assertEqual(503, response.status_code)
self.assertIn(self.UNAVAILABLE, response.text)
self.assertIn('boom', response.text)
self.assertIn('Garbage collector', response.text)
# Note: For whatever reason, detailed=True text has no additonal info
@mock.patch('octavia.db.api.get_session')
def test_healthcheck_get_text_detailed_failed(self, mock_get_session):
self.conf.config(group='healthcheck', detailed=True)
mock_session = mock.MagicMock()
mock_session.execute.side_effect = [Exception('boom')]
mock_get_session.return_value = mock_session
response = self._get(self._get_enabled_app(), '/healthcheck',
status=503)
self.assertEqual(503, response.status_code)
self.assertEqual(self.UNAVAILABLE, response.text)

View File

@ -0,0 +1,6 @@
---
features:
- |
Added the oslo-middleware healthcheck app to the Octavia API.
Hitting /healthcheck will return a 200. This is enabled via the
[api_settings]healthcheck_enabled setting and is disabled by default.

View File

@ -3,7 +3,7 @@
# process, which may cause wedges in the gate later.
alembic>=0.8.10 # MIT
cotyledon>=1.3.0 # Apache-2.0
pecan!=1.0.2,!=1.0.3,!=1.0.4,!=1.2,>=1.0.0 # BSD
pecan>=1.3.2 # BSD
pbr!=2.1.0,>=2.0.0 # Apache-2.0
SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT
Babel!=2.4.0,>=2.3.4 # BSD
@ -13,7 +13,7 @@ rfc3986>=0.3.1 # Apache-2.0
keystoneauth1>=3.4.0 # Apache-2.0
keystonemiddleware>=4.17.0 # Apache-2.0
python-neutronclient>=6.7.0 # Apache-2.0
WebOb>=1.7.1 # MIT
WebOb>=1.8.2 # MIT
stevedore>=1.20.0 # Apache-2.0
oslo.config>=5.2.0 # Apache-2.0
oslo.context>=2.19.2 # Apache-2.0
@ -21,7 +21,7 @@ oslo.db>=4.27.0 # Apache-2.0
oslo.i18n>=3.15.3 # Apache-2.0
oslo.log>=3.36.0 # Apache-2.0
oslo.messaging>=6.3.0 # Apache-2.0
oslo.middleware>=3.31.0 # Apache-2.0
oslo.middleware>=4.0.1 # Apache-2.0
oslo.policy>=1.30.0 # Apache-2.0
oslo.reports>=1.18.0 # Apache-2.0
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0

View File

@ -100,6 +100,8 @@ oslo.policy.policies =
octavia = octavia.policies:list_rules
oslo.policy.enforcer =
octavia = octavia.common.policy:get_no_context_enforcer
oslo.middleware.healthcheck =
octavia_db_check = octavia.api.healthcheck.healthcheck_plugins:OctaviaDBHealthcheck
[compile_catalog]
directory = octavia/locale