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 providers/index.rst
log-offloading.rst log-offloading.rst
api-audit.rst api-audit.rst
healthcheck.rst
flavors.rst flavors.rst
apache-httpd.rst apache-httpd.rst

View File

@ -53,6 +53,9 @@
# The minimum health monitor delay interval for UDP-CONNECT Health Monitor type # The minimum health monitor delay interval for UDP-CONNECT Health Monitor type
# udp_connect_min_interval_health_monitor = 3 # udp_connect_min_interval_health_monitor = 3
# Boolean to enable/disable oslo middleware /healthcheck in the Octavia API
# healthcheck_enabled = False
[database] [database]
# This line MUST be changed to actually run the plugin. # This line MUST be changed to actually run the plugin.
# Example: # Example:
@ -572,3 +575,21 @@
# List of enabled provider agents. # List of enabled provider agents.
# 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.i18n==3.15.3
oslo.log==3.36.0 oslo.log==3.36.0
oslo.messaging==6.3.0 oslo.messaging==6.3.0
oslo.middleware==3.31.0 oslo.middleware==4.0.1
oslo.policy==1.30.0 oslo.policy==1.30.0
oslo.reports==1.18.0 oslo.reports==1.18.0
oslo.serialization==2.18.0 oslo.serialization==2.18.0
@ -99,7 +99,7 @@ paramiko==2.4.1
Paste==2.0.3 Paste==2.0.3
PasteDeploy==1.5.2 PasteDeploy==1.5.2
pbr==2.0.0 pbr==2.0.0
pecan==1.0.0 pecan==1.3.2
pep8==1.7.1 pep8==1.7.1
pika==0.10.0 pika==0.10.0
pika-pool==0.1.3 pika-pool==0.1.3
@ -169,7 +169,7 @@ vine==1.1.4
voluptuous==0.11.1 voluptuous==0.11.1
waitress==1.1.0 waitress==1.1.0
warlock==1.3.0 warlock==1.3.0
WebOb==1.7.1 WebOb==1.8.2
WebTest==2.0.29 WebTest==2.0.29
Werkzeug==0.14.1 Werkzeug==0.14.1
wrapt==1.10.11 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 cors
from oslo_middleware import http_proxy_to_wsgi from oslo_middleware import http_proxy_to_wsgi
from oslo_middleware import request_id 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 import config as app_config
from octavia.api.drivers import driver_factory from octavia.api.drivers import driver_factory
@ -36,7 +37,7 @@ CONF = cfg.CONF
def get_pecan_config(): def get_pecan_config():
"""Returns the pecan config.""" """Returns the pecan config."""
filename = app_config.__file__.replace('.pyc', '.py') filename = app_config.__file__.replace('.pyc', '.py')
return pecan.configuration.conf_from_file(filename) return pecan_configuration.conf_from_file(filename)
def _init_drivers(): def _init_drivers():
@ -56,9 +57,9 @@ def setup_app(pecan_config=None, debug=False, argv=None):
if not pecan_config: if not pecan_config:
pecan_config = get_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, pecan_config.app.root,
wrap_app=_wrap_app, wrap_app=_wrap_app,
debug=debug, 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 # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo_config import cfg
from oslo_log import log as logging 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 request as pecan_request
from pecan import rest
from wsme import types as wtypes from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan from wsmeext import pecan as wsme_pecan
from octavia.api.v2 import controllers as v2_controller from octavia.api.v2 import controllers as v2_controller
CONF = cfg.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class RootController(rest.RestController): class RootController(object):
"""The controller with which the pecan wsgi app should be created.""" """The controller with which the pecan wsgi app should be created."""
def __init__(self): def __init__(self):
super(RootController, self).__init__() super(RootController, self).__init__()
setattr(self, 'v2.0', v2_controller.V2Controller()) setattr(self, 'v2.0', v2_controller.V2Controller())
setattr(self, 'v2', 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, def _add_a_version(self, versions, version, url_version, status,
timestamp, base_url): timestamp, base_url):
@ -45,7 +61,7 @@ class RootController(rest.RestController):
}) })
@wsme_pecan.wsexpose(wtypes.text) @wsme_pecan.wsexpose(wtypes.text)
def get(self): def index(self):
host_url = pecan_request.path_url host_url = pecan_request.path_url
if not host_url.endswith('/'): if not host_url.endswith('/'):

View File

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

View File

@ -17,7 +17,7 @@ from oslo_log import log as logging
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_utils import excutils from oslo_utils import excutils
from oslo_utils import uuidutils from oslo_utils import uuidutils
import pecan from pecan import request as pecan_request
from sqlalchemy.orm import exc as sa_exception from sqlalchemy.orm import exc as sa_exception
from wsme import types as wtypes from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan from wsmeext import pecan as wsme_pecan
@ -43,7 +43,7 @@ class AvailabilityZoneProfileController(base.BaseController):
wtypes.text, [wtypes.text], ignore_extra_args=True) wtypes.text, [wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None): def get_one(self, id, fields=None):
"""Gets an Availability Zone Profile's detail.""" """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, self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ONE) constants.RBAC_GET_ONE)
if id == constants.NIL_UUID: if id == constants.NIL_UUID:
@ -63,7 +63,7 @@ class AvailabilityZoneProfileController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_all(self, fields=None): def get_all(self, fields=None):
"""Lists all Availability Zone Profiles.""" """Lists all Availability Zone Profiles."""
pcontext = pecan.request.context pcontext = pecan_request.context
context = pcontext.get('octavia_context') context = pcontext.get('octavia_context')
self._auth_validate_action(context, context.project_id, self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ALL) constants.RBAC_GET_ALL)
@ -87,7 +87,7 @@ class AvailabilityZoneProfileController(base.BaseController):
"""Creates an Availability Zone Profile.""" """Creates an Availability Zone Profile."""
availability_zone_profile = ( availability_zone_profile = (
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, self._auth_validate_action(context, context.project_id,
constants.RBAC_POST) constants.RBAC_POST)
# Do a basic JSON validation on the metadata # Do a basic JSON validation on the metadata
@ -155,7 +155,7 @@ class AvailabilityZoneProfileController(base.BaseController):
"""Updates an Availability Zone Profile.""" """Updates an Availability Zone Profile."""
availability_zone_profile = ( availability_zone_profile = (
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, self._auth_validate_action(context, context.project_id,
constants.RBAC_PUT) constants.RBAC_PUT)
@ -216,7 +216,7 @@ class AvailabilityZoneProfileController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204) @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, availability_zone_profile_id): def delete(self, availability_zone_profile_id):
"""Deletes an Availability Zone Profile""" """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, self._auth_validate_action(context, context.project_id,
constants.RBAC_DELETE) 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_db import exception as odb_exceptions
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils from oslo_utils import excutils
import pecan from pecan import request as pecan_request
from sqlalchemy.orm import exc as sa_exception from sqlalchemy.orm import exc as sa_exception
from wsme import types as wtypes from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan from wsmeext import pecan as wsme_pecan
@ -40,7 +40,7 @@ class AvailabilityZonesController(base.BaseController):
wtypes.text, [wtypes.text], ignore_extra_args=True) wtypes.text, [wtypes.text], ignore_extra_args=True)
def get_one(self, name, fields=None): def get_one(self, name, fields=None):
"""Gets an Availability Zone's detail.""" """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, self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ONE) constants.RBAC_GET_ONE)
if name == constants.NIL_UUID: if name == constants.NIL_UUID:
@ -60,7 +60,7 @@ class AvailabilityZonesController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_all(self, fields=None): def get_all(self, fields=None):
"""Lists all Availability Zones.""" """Lists all Availability Zones."""
pcontext = pecan.request.context pcontext = pecan_request.context
context = pcontext.get('octavia_context') context = pcontext.get('octavia_context')
self._auth_validate_action(context, context.project_id, self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ALL) constants.RBAC_GET_ALL)
@ -82,7 +82,7 @@ class AvailabilityZonesController(base.BaseController):
def post(self, availability_zone_): def post(self, availability_zone_):
"""Creates an Availability Zone.""" """Creates an Availability Zone."""
availability_zone = availability_zone_.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, self._auth_validate_action(context, context.project_id,
constants.RBAC_POST) constants.RBAC_POST)
@ -111,7 +111,7 @@ class AvailabilityZonesController(base.BaseController):
body=availability_zone_types.AvailabilityZoneRootPUT) body=availability_zone_types.AvailabilityZoneRootPUT)
def put(self, name, availability_zone_): def put(self, name, availability_zone_):
availability_zone = availability_zone_.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, self._auth_validate_action(context, context.project_id,
constants.RBAC_PUT) constants.RBAC_PUT)
if name == constants.NIL_UUID: if name == constants.NIL_UUID:
@ -144,7 +144,7 @@ class AvailabilityZonesController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204) @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, availability_zone_name): def delete(self, availability_zone_name):
"""Deletes an Availability Zone""" """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, self._auth_validate_action(context, context.project_id,
constants.RBAC_DELETE) constants.RBAC_DELETE)

View File

@ -16,7 +16,8 @@ from cryptography.hazmat.backends import default_backend
from cryptography import x509 from cryptography import x509
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging 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 stevedore import driver as stevedore_driver
from wsme import types as wtypes from wsme import types as wtypes
@ -31,7 +32,7 @@ CONF = cfg.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class BaseController(pecan.rest.RestController): class BaseController(pecan_rest.RestController):
RBAC_TYPE = None RBAC_TYPE = None
def __init__(self): def __init__(self):
@ -257,7 +258,7 @@ class BaseController(pecan.rest.RestController):
return attrs return attrs
def _validate_tls_refs(self, tls_refs): def _validate_tls_refs(self, tls_refs):
context = pecan.request.context.get('octavia_context') context = pecan_request.context.get('octavia_context')
bad_refs = [] bad_refs = []
for ref in tls_refs: for ref in tls_refs:
try: try:
@ -272,7 +273,7 @@ class BaseController(pecan.rest.RestController):
raise exceptions.CertificateRetrievalException(ref=bad_refs) raise exceptions.CertificateRetrievalException(ref=bad_refs)
def _validate_client_ca_and_crl_refs(self, client_ca_ref, crl_ref): 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 = [] bad_refs = []
try: try:
self.cert_manager.set_acls(context, client_ca_ref) 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_serialization import jsonutils
from oslo_utils import excutils from oslo_utils import excutils
from oslo_utils import uuidutils from oslo_utils import uuidutils
import pecan from pecan import request as pecan_request
from sqlalchemy.orm import exc as sa_exception from sqlalchemy.orm import exc as sa_exception
from wsme import types as wtypes from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan from wsmeext import pecan as wsme_pecan
@ -44,7 +44,7 @@ class FlavorProfileController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None): def get_one(self, id, fields=None):
"""Gets a flavor profile's detail.""" """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, self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ONE) constants.RBAC_GET_ONE)
if id == constants.NIL_UUID: if id == constants.NIL_UUID:
@ -61,7 +61,7 @@ class FlavorProfileController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_all(self, fields=None): def get_all(self, fields=None):
"""Lists all flavor profiles.""" """Lists all flavor profiles."""
pcontext = pecan.request.context pcontext = pecan_request.context
context = pcontext.get('octavia_context') context = pcontext.get('octavia_context')
self._auth_validate_action(context, context.project_id, self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ALL) constants.RBAC_GET_ALL)
@ -81,7 +81,7 @@ class FlavorProfileController(base.BaseController):
def post(self, flavor_profile_): def post(self, flavor_profile_):
"""Creates a flavor Profile.""" """Creates a flavor Profile."""
flavorprofile = flavor_profile_.flavorprofile 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, self._auth_validate_action(context, context.project_id,
constants.RBAC_POST) constants.RBAC_POST)
# Do a basic JSON validation on the metadata # Do a basic JSON validation on the metadata
@ -138,7 +138,7 @@ class FlavorProfileController(base.BaseController):
def put(self, id, flavor_profile_): def put(self, id, flavor_profile_):
"""Updates a flavor Profile.""" """Updates a flavor Profile."""
flavorprofile = flavor_profile_.flavorprofile 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, self._auth_validate_action(context, context.project_id,
constants.RBAC_PUT) constants.RBAC_PUT)
@ -190,7 +190,7 @@ class FlavorProfileController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204) @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, flavor_profile_id): def delete(self, flavor_profile_id):
"""Deletes a Flavor Profile""" """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, self._auth_validate_action(context, context.project_id,
constants.RBAC_DELETE) 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_log import log as logging
from oslo_utils import excutils from oslo_utils import excutils
from oslo_utils import uuidutils from oslo_utils import uuidutils
import pecan from pecan import request as pecan_request
from sqlalchemy.orm import exc as sa_exception from sqlalchemy.orm import exc as sa_exception
from wsme import types as wtypes from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan from wsmeext import pecan as wsme_pecan
@ -42,7 +42,7 @@ class FlavorsController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None): def get_one(self, id, fields=None):
"""Gets a flavor's detail.""" """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, self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ONE) constants.RBAC_GET_ONE)
if id == constants.NIL_UUID: if id == constants.NIL_UUID:
@ -58,7 +58,7 @@ class FlavorsController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_all(self, fields=None): def get_all(self, fields=None):
"""Lists all flavors.""" """Lists all flavors."""
pcontext = pecan.request.context pcontext = pecan_request.context
context = pcontext.get('octavia_context') context = pcontext.get('octavia_context')
self._auth_validate_action(context, context.project_id, self._auth_validate_action(context, context.project_id,
constants.RBAC_GET_ALL) constants.RBAC_GET_ALL)
@ -77,7 +77,7 @@ class FlavorsController(base.BaseController):
def post(self, flavor_): def post(self, flavor_):
"""Creates a flavor.""" """Creates a flavor."""
flavor = flavor_.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, self._auth_validate_action(context, context.project_id,
constants.RBAC_POST) constants.RBAC_POST)
@ -106,7 +106,7 @@ class FlavorsController(base.BaseController):
body=flavor_types.FlavorRootPUT) body=flavor_types.FlavorRootPUT)
def put(self, id, flavor_): def put(self, id, flavor_):
flavor = flavor_.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, self._auth_validate_action(context, context.project_id,
constants.RBAC_PUT) constants.RBAC_PUT)
if id == constants.NIL_UUID: if id == constants.NIL_UUID:
@ -134,7 +134,7 @@ class FlavorsController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204) @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, flavor_id): def delete(self, flavor_id):
"""Deletes a Flavor""" """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, self._auth_validate_action(context, context.project_id,
constants.RBAC_DELETE) constants.RBAC_DELETE)

View File

@ -17,7 +17,7 @@ from oslo_config import cfg
from oslo_db import exception as odb_exceptions from oslo_db import exception as odb_exceptions
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils from oslo_utils import excutils
import pecan from pecan import request as pecan_request
from wsme import types as wtypes from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan from wsmeext import pecan as wsme_pecan
@ -48,7 +48,7 @@ class HealthMonitorController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None): def get_one(self, id, fields=None):
"""Gets a single healthmonitor's details.""" """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) db_hm = self._get_db_hm(context.session, id, show_deleted=False)
self._auth_validate_action(context, db_hm.project_id, self._auth_validate_action(context, db_hm.project_id,
@ -64,7 +64,7 @@ class HealthMonitorController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_all(self, project_id=None, fields=None): def get_all(self, project_id=None, fields=None):
"""Gets all health monitors.""" """Gets all health monitors."""
pcontext = pecan.request.context pcontext = pecan_request.context
context = pcontext.get('octavia_context') context = pcontext.get('octavia_context')
query_filter = self._auth_get_all(context, project_id) query_filter = self._auth_get_all(context, project_id)
@ -196,7 +196,7 @@ class HealthMonitorController(base.BaseController):
body=hm_types.HealthMonitorRootPOST, status_code=201) body=hm_types.HealthMonitorRootPOST, status_code=201)
def post(self, health_monitor_): def post(self, health_monitor_):
"""Creates a health monitor on a pool.""" """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 health_monitor = health_monitor_.healthmonitor
if (not CONF.api_settings.allow_ping_health_monitors and 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) body=hm_types.HealthMonitorRootPUT, status_code=200)
def put(self, id, health_monitor_): def put(self, id, health_monitor_):
"""Updates a 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 health_monitor = health_monitor_.healthmonitor
db_hm = self._get_db_hm(context.session, id, show_deleted=False) 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) @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, id): def delete(self, id):
"""Deletes a health monitor.""" """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) db_hm = self._get_db_hm(context.session, id, show_deleted=False)
pool = self._get_db_pool(context.session, db_hm.pool_id) 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_db import exception as odb_exceptions
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils 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 wsme import types as wtypes
from wsmeext import pecan as wsme_pecan from wsmeext import pecan as wsme_pecan
@ -48,7 +49,7 @@ class L7PolicyController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get(self, id, fields=None): def get(self, id, fields=None):
"""Gets a single l7policy's details.""" """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, db_l7policy = self._get_db_l7policy(context.session, id,
show_deleted=False) show_deleted=False)
@ -65,7 +66,7 @@ class L7PolicyController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_all(self, project_id=None, fields=None): def get_all(self, project_id=None, fields=None):
"""Lists all l7policies of a listener.""" """Lists all l7policies of a listener."""
pcontext = pecan.request.context pcontext = pecan_request.context
context = pcontext.get('octavia_context') context = pcontext.get('octavia_context')
query_filter = self._auth_get_all(context, project_id) query_filter = self._auth_get_all(context, project_id)
@ -115,7 +116,7 @@ class L7PolicyController(base.BaseController):
def post(self, l7policy_): def post(self, l7policy_):
"""Creates a l7policy on a listener.""" """Creates a l7policy on a listener."""
l7policy = l7policy_.l7policy l7policy = l7policy_.l7policy
context = pecan.request.context.get('octavia_context') context = pecan_request.context.get('octavia_context')
# Verify the parent listener exists # Verify the parent listener exists
listener_id = l7policy.listener_id listener_id = l7policy.listener_id
listener = self._get_db_listener( listener = self._get_db_listener(
@ -206,7 +207,7 @@ class L7PolicyController(base.BaseController):
if val in l7policy_dict: if val in l7policy_dict:
l7policy_dict[attr] = l7policy_dict.pop(val) l7policy_dict[attr] = l7policy_dict.pop(val)
sanitized_l7policy = l7policy_types.L7PolicyPUT(**l7policy_dict) 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, db_l7policy = self._get_db_l7policy(context.session, id,
show_deleted=False) show_deleted=False)
@ -268,7 +269,7 @@ class L7PolicyController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204) @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, id): def delete(self, id):
"""Deletes a l7policy.""" """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, db_l7policy = self._get_db_l7policy(context.session, id,
show_deleted=False) show_deleted=False)
load_balancer_id, listener_id = self._get_listener_and_loadbalancer_id( 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, driver_utils.call_provider(driver.name, driver.l7policy_delete,
provider_l7policy) provider_l7policy)
@pecan.expose() @pecan_expose()
def _lookup(self, l7policy_id, *remainder): def _lookup(self, l7policy_id, *remainder):
"""Overridden pecan _lookup method for custom routing. """Overridden pecan _lookup method for custom routing.
Verifies that the l7policy passed in the url exists, and if so decides Verifies that the l7policy passed in the url exists, and if so decides
which controller, if any, should control be passed. 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': if l7policy_id and remainder and remainder[0] == 'rules':
remainder = remainder[1:] remainder = remainder[1:]
db_l7policy = self.repositories.l7policy.get( db_l7policy = self.repositories.l7policy.get(

View File

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

View File

@ -17,7 +17,8 @@ from oslo_config import cfg
from oslo_db import exception as odb_exceptions from oslo_db import exception as odb_exceptions
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils 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 wsme import types as wtypes
from wsmeext import pecan as wsme_pecan from wsmeext import pecan as wsme_pecan
@ -51,7 +52,7 @@ class ListenersController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None): def get_one(self, id, fields=None):
"""Gets a single listener's details.""" """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, db_listener = self._get_db_listener(context.session, id,
show_deleted=False) show_deleted=False)
@ -72,7 +73,7 @@ class ListenersController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_all(self, project_id=None, fields=None): def get_all(self, project_id=None, fields=None):
"""Lists all listeners.""" """Lists all listeners."""
pcontext = pecan.request.context pcontext = pecan_request.context
context = pcontext.get('octavia_context') context = pcontext.get('octavia_context')
query_filter = self._auth_get_all(context, project_id) 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 # Validate that the L4 protocol (UDP or TCP) is not already used for
# the specified protocol_port in this load balancer # the specified protocol_port in this load balancer
pcontext = pecan.request.context pcontext = pecan_request.context
query_filter = { query_filter = {
'project_id': listener_dict['project_id'], 'project_id': listener_dict['project_id'],
'load_balancer_id': listener_dict['load_balancer_id'], 'load_balancer_id': listener_dict['load_balancer_id'],
@ -310,7 +311,7 @@ class ListenersController(base.BaseController):
def post(self, listener_): def post(self, listener_):
"""Creates a listener on a load balancer.""" """Creates a listener on a load balancer."""
listener = listener_.listener listener = listener_.listener
context = pecan.request.context.get('octavia_context') context = pecan_request.context.get('octavia_context')
load_balancer_id = listener.loadbalancer_id load_balancer_id = listener.loadbalancer_id
listener.project_id, provider = self._get_lb_project_id_provider( listener.project_id, provider = self._get_lb_project_id_provider(
@ -507,7 +508,7 @@ class ListenersController(base.BaseController):
def put(self, id, listener_): def put(self, id, listener_):
"""Updates a listener on a load balancer.""" """Updates a listener on a load balancer."""
listener = listener_.listener 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, db_listener = self._get_db_listener(context.session, id,
show_deleted=False) show_deleted=False)
load_balancer_id = db_listener.load_balancer_id 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) @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, id): def delete(self, id):
"""Deletes a listener from a load balancer.""" """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, db_listener = self._get_db_listener(context.session, id,
show_deleted=False) show_deleted=False)
load_balancer_id = db_listener.load_balancer_id 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, driver_utils.call_provider(driver.name, driver.listener_delete,
provider_listener) provider_listener)
@pecan.expose() @pecan_expose()
def _lookup(self, id, *remainder): def _lookup(self, id, *remainder):
"""Overridden pecan _lookup method for custom routing. """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, @wsme_pecan.wsexpose(listener_types.StatisticsRootResponse, wtypes.text,
status_code=200) status_code=200)
def get(self): 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, db_listener = self._get_db_listener(context.session, self.id,
show_deleted=False) show_deleted=False)
if not db_listener: 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_log import log as logging
from oslo_utils import excutils from oslo_utils import excutils
from oslo_utils import strutils 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 sqlalchemy.orm import exc as sa_exception
from wsme import types as wtypes from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan from wsmeext import pecan as wsme_pecan
@ -56,7 +57,7 @@ class LoadBalancersController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_one(self, id, fields=None): def get_one(self, id, fields=None):
"""Gets a single load balancer's details.""" """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, load_balancer = self._get_db_lb(context.session, id,
show_deleted=False) show_deleted=False)
@ -78,7 +79,7 @@ class LoadBalancersController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_all(self, project_id=None, fields=None): def get_all(self, project_id=None, fields=None):
"""Lists all load balancers.""" """Lists all load balancers."""
pcontext = pecan.request.context pcontext = pecan_request.context
context = pcontext.get('octavia_context') context = pcontext.get('octavia_context')
query_filter = self._auth_get_all(context, project_id) query_filter = self._auth_get_all(context, project_id)
@ -373,7 +374,7 @@ class LoadBalancersController(base.BaseController):
def post(self, load_balancer): def post(self, load_balancer):
"""Creates a load balancer.""" """Creates a load balancer."""
load_balancer = load_balancer.loadbalancer 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: if not load_balancer.project_id and context.project_id:
load_balancer.project_id = context.project_id load_balancer.project_id = context.project_id
@ -607,7 +608,7 @@ class LoadBalancersController(base.BaseController):
def put(self, id, load_balancer): def put(self, id, load_balancer):
"""Updates a load balancer.""" """Updates a load balancer."""
load_balancer = load_balancer.loadbalancer 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) db_lb = self._get_db_lb(context.session, id, show_deleted=False)
self._auth_validate_action(context, db_lb.project_id, 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) @wsme_pecan.wsexpose(None, wtypes.text, wtypes.text, status_code=204)
def delete(self, id, cascade=False): def delete(self, id, cascade=False):
"""Deletes a load balancer.""" """Deletes a load balancer."""
context = pecan.request.context.get('octavia_context') context = pecan_request.context.get('octavia_context')
cascade = strutils.bool_from_string(cascade) cascade = strutils.bool_from_string(cascade)
db_lb = self._get_db_lb(context.session, id, show_deleted=False) 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, driver_utils.call_provider(driver.name, driver.loadbalancer_delete,
provider_loadbalancer, cascade) provider_loadbalancer, cascade)
@pecan.expose() @pecan_expose()
def _lookup(self, id, *remainder): def _lookup(self, id, *remainder):
"""Overridden pecan _lookup method for custom routing. """Overridden pecan _lookup method for custom routing.
@ -731,7 +732,7 @@ class StatusController(base.BaseController):
@wsme_pecan.wsexpose(lb_types.StatusRootResponse, wtypes.text, @wsme_pecan.wsexpose(lb_types.StatusRootResponse, wtypes.text,
status_code=200) status_code=200)
def get(self): 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, load_balancer = self._get_db_lb(context.session, self.id,
show_deleted=False) show_deleted=False)
if not load_balancer: if not load_balancer:
@ -759,7 +760,7 @@ class StatisticsController(base.BaseController, stats.StatsMixin):
@wsme_pecan.wsexpose(lb_types.StatisticsRootResponse, wtypes.text, @wsme_pecan.wsexpose(lb_types.StatisticsRootResponse, wtypes.text,
status_code=200) status_code=200)
def get(self): 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, load_balancer = self._get_db_lb(context.session, self.id,
show_deleted=False) show_deleted=False)
if not load_balancer: if not load_balancer:
@ -787,7 +788,7 @@ class FailoverController(LoadBalancersController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=202) @wsme_pecan.wsexpose(None, wtypes.text, status_code=202)
def put(self, **kwargs): def put(self, **kwargs):
"""Fails over a loadbalancer""" """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, db_lb = self._get_db_lb(context.session, self.lb_id,
show_deleted=False) 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_log import log as logging
from oslo_utils import excutils from oslo_utils import excutils
from oslo_utils import strutils from oslo_utils import strutils
import pecan from pecan import request as pecan_request
from wsme import types as wtypes from wsme import types as wtypes
from wsmeext import pecan as wsme_pecan from wsmeext import pecan as wsme_pecan
@ -48,7 +48,7 @@ class MemberController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get(self, id, fields=None): def get(self, id, fields=None):
"""Gets a single pool member's details.""" """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, db_member = self._get_db_member(context.session, id,
show_deleted=False) show_deleted=False)
@ -67,7 +67,7 @@ class MemberController(base.BaseController):
ignore_extra_args=True) ignore_extra_args=True)
def get_all(self, fields=None): def get_all(self, fields=None):
"""Lists all pool members of a pool.""" """Lists all pool members of a pool."""
pcontext = pecan.request.context pcontext = pecan_request.context
context = pcontext.get('octavia_context') context = pcontext.get('octavia_context')
pool = self._get_db_pool(context.session, self.pool_id, pool = self._get_db_pool(context.session, self.pool_id,
@ -144,7 +144,7 @@ class MemberController(base.BaseController):
def post(self, member_): def post(self, member_):
"""Creates a pool member on a pool.""" """Creates a pool member on a pool."""
member = member_.member member = member_.member
context = pecan.request.context.get('octavia_context') context = pecan_request.context.get('octavia_context')
validate.ip_not_reserved(member.address) validate.ip_not_reserved(member.address)
@ -229,7 +229,7 @@ class MemberController(base.BaseController):
def put(self, id, member_): def put(self, id, member_):
"""Updates a pool member.""" """Updates a pool member."""
member = member_.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, db_member = self._get_db_member(context.session, id,
show_deleted=False) show_deleted=False)
@ -285,7 +285,7 @@ class MemberController(base.BaseController):
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204) @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, id): def delete(self, id):
"""Deletes a pool member.""" """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, db_member = self._get_db_member(context.session, id,
show_deleted=False) show_deleted=False)
@ -327,7 +327,7 @@ class MembersController(MemberController):
"""Updates all members.""" """Updates all members."""
members = members_.members members = members_.members
additive_only = strutils.bool_from_string(additive_only) 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) db_pool = self._get_db_pool(context.session, self.pool_id)
old_members = db_pool.members 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_db import exception as odb_exceptions
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils 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 wsme import types as wtypes
from wsmeext import pecan as wsme_pecan from wsmeext import pecan as wsme_pecan
@ -51,7 +52,7 @@ class PoolsController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get(self, id, fields=None): def get(self, id, fields=None):
"""Gets a pool's details.""" """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) db_pool = self._get_db_pool(context.session, id, show_deleted=False)
self._auth_validate_action(context, db_pool.project_id, self._auth_validate_action(context, db_pool.project_id,
@ -66,7 +67,7 @@ class PoolsController(base.BaseController):
[wtypes.text], ignore_extra_args=True) [wtypes.text], ignore_extra_args=True)
def get_all(self, project_id=None, fields=None): def get_all(self, project_id=None, fields=None):
"""Lists all pools.""" """Lists all pools."""
pcontext = pecan.request.context pcontext = pecan_request.context
context = pcontext.get('octavia_context') context = pcontext.get('octavia_context')
query_filter = self._auth_get_all(context, project_id) 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 # For some API requests the listener_id will be passed in the
# pool_dict: # pool_dict:
pool = pool_.pool pool = pool_.pool
context = pecan.request.context.get('octavia_context') context = pecan_request.context.get('octavia_context')
if pool.protocol == constants.PROTOCOL_UDP: if pool.protocol == constants.PROTOCOL_UDP:
self._validate_pool_request_for_udp(pool) self._validate_pool_request_for_udp(pool)
else: else:
@ -372,7 +373,7 @@ class PoolsController(base.BaseController):
def put(self, id, pool_): def put(self, id, pool_):
"""Updates a pool on a load balancer.""" """Updates a pool on a load balancer."""
pool = pool_.pool 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) db_pool = self._get_db_pool(context.session, id, show_deleted=False)
project_id, provider = self._get_lb_project_id_provider( 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) @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
def delete(self, id): def delete(self, id):
"""Deletes a pool from a load balancer.""" """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) db_pool = self._get_db_pool(context.session, id, show_deleted=False)
if db_pool.l7policies: if db_pool.l7policies:
raise exceptions.PoolInUseByL7Policy( raise exceptions.PoolInUseByL7Policy(
@ -458,14 +459,14 @@ class PoolsController(base.BaseController):
driver_utils.call_provider(driver.name, driver.pool_delete, driver_utils.call_provider(driver.name, driver.pool_delete,
provider_pool) provider_pool)
@pecan.expose() @pecan_expose()
def _lookup(self, pool_id, *remainder): def _lookup(self, pool_id, *remainder):
"""Overridden pecan _lookup method for custom routing. """Overridden pecan _lookup method for custom routing.
Verifies that the pool passed in the url exists, and if so decides Verifies that the pool passed in the url exists, and if so decides
which controller, if any, should control be passed. 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': if pool_id and remainder and remainder[0] == 'members':
remainder = remainder[1:] remainder = remainder[1:]
db_pool = self.repositories.pool.get(context.session, id=pool_id) db_pool = self.repositories.pool.get(context.session, id=pool_id)

View File

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

View File

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

View File

@ -100,6 +100,9 @@ api_opts = [
help=_("The minimum health monitor delay interval for the " help=_("The minimum health monitor delay interval for the "
"UDP-CONNECT Health Monitor type. A negative integer " "UDP-CONNECT Health Monitor type. A negative integer "
"value means 'no limit'.")), "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 # Options only used by the amphora agent

View File

@ -21,7 +21,8 @@ from octavia.common import constants
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
_NOAUTH_PATHS = ['/', '/load-balancer/'] _NOAUTH_PATHS = ['/', '/load-balancer/', '/healthcheck',
'/load-balancer/healthcheck']
class KeystoneSession(object): 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. # process, which may cause wedges in the gate later.
alembic>=0.8.10 # MIT alembic>=0.8.10 # MIT
cotyledon>=1.3.0 # Apache-2.0 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 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 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 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 keystoneauth1>=3.4.0 # Apache-2.0
keystonemiddleware>=4.17.0 # Apache-2.0 keystonemiddleware>=4.17.0 # Apache-2.0
python-neutronclient>=6.7.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 stevedore>=1.20.0 # Apache-2.0
oslo.config>=5.2.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0
oslo.context>=2.19.2 # 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.i18n>=3.15.3 # Apache-2.0
oslo.log>=3.36.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0
oslo.messaging>=6.3.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.policy>=1.30.0 # Apache-2.0
oslo.reports>=1.18.0 # Apache-2.0 oslo.reports>=1.18.0 # Apache-2.0
oslo.serialization!=2.19.1,>=2.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 octavia = octavia.policies:list_rules
oslo.policy.enforcer = oslo.policy.enforcer =
octavia = octavia.common.policy:get_no_context_enforcer octavia = octavia.common.policy:get_no_context_enforcer
oslo.middleware.healthcheck =
octavia_db_check = octavia.api.healthcheck.healthcheck_plugins:OctaviaDBHealthcheck
[compile_catalog] [compile_catalog]
directory = octavia/locale directory = octavia/locale